SMTP Ghost API

Lists & Campaigns API Documentation

Overview

The SMTP Ghost API allows you to manage contact lists and schedule emails for your campaigns. Our API is RESTful, returns JSON responses, and uses standard HTTP status codes.

Get Started

Sign up for an account to get your API key and start integrating with our platform.

Authentication

All API requests require authentication using your API key. Include your API key in the request headers:

X-API-KEY: your_api_key_here

You can find your API key in your account's developer settings after signing up.

Base URL

https://smtpghost.com/api/v1

Lists Endpoints

Get All Lists

GET /api/v1/lists

Retrieve all lists in your organization with campaign and contact information.

Example Response
{
  "status": "success",
  "data": [
    {
      "id": 123,
      "uuid": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Marketing Leads Q1",
      "contacts_count": 1500,
      "created_at": "2024-01-15T10:30:00Z",
      "updated_at": "2024-01-20T15:45:00Z",
      "has_campaign": true,
      "campaign_id": 456,
      "campaign_name": "Q1 Outreach"
    }
  ],
  "total": 5
}

Get Single List

GET /api/v1/lists/:uuid

Retrieve details of a specific list by UUID.

Example Response
{
  "status": "success",
  "data": {
    "id": 123,
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Marketing Leads Q1",
    "contacts_count": 1500,
    "created_at": "2024-01-15T10:30:00Z",
    "updated_at": "2024-01-20T15:45:00Z",
    "has_campaign": true,
    "campaign_id": 456,
    "campaign_name": "Q1 Outreach"
  }
}

Create New List

POST /api/v1/lists

Create a new contact list.

Request Body
{
  "list": {
    "name": "New Customer List"
  }
}
Example Response
{
  "status": "success",
  "message": "List created successfully",
  "data": {
    "id": 124,
    "uuid": "660e8400-e29b-41d4-a716-446655440001",
    "name": "New Customer List",
    "contacts_count": 0
  }
}

Get Contacts from List

GET /api/v1/lists/:uuid/contacts

Retrieve all contacts from a specific list with their fields and email validation status.

Query Parameters:

page
(integer, optional) - Page number for pagination (default: 1)
per_page
(integer, optional) - Number of contacts per page (default: 100, max: 1000)
Example Response
{
  "status": "success",
  "data": {
    "contacts": [
      {
        "id": 123456,
        "email": "john.doe@example.com",
        "email_valid": true,
        "created_at": "2024-01-15T10:30:00Z",
        "updated_at": "2024-01-20T15:45:00Z",
        "fields": {
          "first_name": "John",
          "last_name": "Doe",
          "company": "Acme Corp",
          "title": "CEO",
          "phone": "+1234567890",
          "industry": "Technology",
          "website": "acme.com"
        }
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page": 100,
      "total_pages": 3,
      "total_contacts": 250
    }
  }
}

Add Contacts to List

POST /api/v1/lists/:uuid/contacts

Add contacts to an existing list. Supports bulk import up to 1000 contacts per request.

Query Parameters:

remove_duplicates
(boolean, optional) - If true, removes duplicate emails within the organization and adds existing contacts to the list without creating duplicates. Default: false.
auto_clean
(boolean, optional) - If true, automatically queues contacts for email verification after import. Default: false.

Contact Data Structure:

Email: Stored directly on the contact record

email *required - Contact's email address

Standard Fields: Stored as ContactField records with predefined names

first_name - First name
last_name - Last name
phone - Phone number
company - Company name
title - Job title

Custom Fields: Also stored as ContactField records with any name you choose

custom_fields - Object with any key-value pairs (industry, budget, lead_source, etc.)

Note: All fields except email are stored as ContactField records. The distinction between "standard" and "custom" fields is just for API convenience - they're implemented identically.

Complete Request Example
POST /api/v1/lists/550e8400-e29b-41d4-a716-446655440000/contacts?remove_duplicates=true&auto_clean=true

{
  "contacts": [
    {
      "email": "john.doe@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "phone": "+1-555-123-4567",
      "company": "Acme Corporation",
      "title": "CEO",
      "custom_fields": {
        "industry": "Technology",
        "company_size": "50-100",
        "location": "San Francisco, CA",
        "lead_source": "Website",
        "budget": "10k+",
        "linkedin": "https://linkedin.com/in/johndoe",
        "website": "https://acme.com",
        "notes": "Interested in enterprise solution"
      }
    },
    {
      "email": "jane.smith@techcorp.com",
      "first_name": "Jane",
      "last_name": "Smith",
      "company": "TechCorp",
      "title": "CTO",
      "custom_fields": {
        "industry": "SaaS",
        "company_size": "100-500",
        "lead_source": "LinkedIn"
      }
    }
  ]
}
Example Response
{
  "status": "success",
  "message": "Successfully processed contacts",
  "data": {
    "total_submitted": 2,
    "total_added": 2,
    "new_contacts": 1,
    "existing_contacts_added": 1,
    "duplicates_removed": 0,
    "invalid_emails": [],
    "list_uuid": "550e8400-e29b-41d4-a716-446655440000",
    "contacts_count": 1502
  }
}

Parameter Usage Examples

Skip duplicates: ?remove_duplicates=true - Existing contacts in your organization won't be duplicated
Enable email verification: ?auto_clean=true - Contacts will be queued for email validation
Both options: ?remove_duplicates=true&auto_clean=true - Recommended for clean data

Campaigns Endpoints

Schedule Contacts for Campaign

POST /api/v1/campaigns/:uuid/schedule_contacts

Add contacts to an evergreen campaign and schedule them for email delivery. This endpoint combines adding contacts to the campaign's list and scheduling emails in a single operation.

Requirements

  • • Campaign must be evergreen (non-evergreen campaigns will return an error)
  • • Campaign must have at least one associated list

Behavior by Campaign Status

  • Active campaigns (sending, scheduled): Contacts added and emails scheduled immediately
  • Paused campaigns: Contacts added but emails NOT scheduled until campaign is resumed
  • Non-evergreen campaigns: Returns error regardless of status

Request Parameters:

remove_duplicates
(boolean, optional) - If true, skips contacts that already exist in the organization. When false, existing contacts will be added to the campaign's list if not already there. Default: false.

Contact Data Structure:

Uses the same field structure as the Lists API:

email *required - Stored on contact record
Standard fields (stored as ContactField): first_name, last_name, phone, company, title
Custom fields (also stored as ContactField): Any key-value pairs in custom_fields object
Request Body
{
  "contacts": [
    {
      "email": "john.doe@example.com",
      "first_name": "John",
      "last_name": "Doe",
      "phone": "+1234567890",
      "company": "Acme Corp",
      "title": "CEO",
      "custom_fields": {
        "industry": "Technology",
        "source": "Website",
        "budget": "10k+"
      }
    }
  ],
  "remove_duplicates": false
}
Success Response (Active Campaign)
{
  "status": "success",
  "message": "1 new contacts added and will be scheduled shortly",
  "data": {
    "campaign": {
      "uuid": "91196fbbffcd9580e8982c214b09577e7ddcebc1",
      "name": "Q1 Outreach Campaign",
      "status": "sending",
      "evergreen": true
    },
    "list": {
      "uuid": "9a3e2346",
      "name": "Campaign Prospects",
      "total_contacts": 150
    },
    "contacts_processed": {
      "total_submitted": 1,
      "new_contacts_added": 1,
      "duplicates_skipped": 0,
      "invalid_emails": []
    },
    "scheduled": true
  }
}
Success Response (Paused Campaign)
{
  "status": "success",
  "message": "1 new contacts added to paused campaign. Resume campaign to schedule emails.",
  "data": {
    "campaign": {
      "uuid": "b30f0377cb4c977e05ca28163a3add33fbc12695",
      "name": "Paused Campaign",
      "status": "paused",
      "evergreen": true
    },
    "contacts_processed": {
      "total_submitted": 1,
      "new_contacts_added": 1,
      "duplicates_skipped": 0,
      "invalid_emails": []
    },
    "scheduled": false
  }
}
Error Response (Non-Evergreen Campaign)
{
  "status": "error",
  "message": "Only evergreen campaigns can schedule new contacts. Please create an evergreen campaign.",
  "current_state": "complete",
  "evergreen": false
}

Interactive Examples

These examples use your actual API key and UUIDs. Select different lists or campaigns to see the examples update automatically.

No lists available - create a list first

No evergreen campaigns available - create an evergreen campaign first

cURL Examples

Create a new list:

curl -X POST https://smtpghost.com/api/v1/lists \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "list": {
      "name": "My New Contact List"
    }
  }'

Add contacts to a list:

curl -X POST "https://smtpghost.com/api/v1/lists/your-list-uuid/contacts?remove_duplicates=true&auto_clean=true" \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      {
        "email": "contact@example.com",
        "first_name": "John",
        "last_name": "Doe",
        "phone": "+1-555-123-4567",
        "company": "Acme Corp",
        "title": "CEO",
        "custom_fields": {
          "industry": "Technology",
          "company_size": "50-100",
          "lead_source": "Website",
          "budget": "10k+",
          "location": "San Francisco, CA"
        }
      }
    ]
  }'

Schedule contacts for campaign:

curl -X POST https://smtpghost.com/api/v1/campaigns/your-campaign-uuid/schedule_contacts \
  -H "X-API-KEY: your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "contacts": [
      {
        "email": "prospect@example.com",
        "first_name": "Jane",
        "last_name": "Smith",
        "company": "Target Corp",
        "custom_fields": {
          "industry": "SaaS",
          "lead_source": "LinkedIn",
          "company_size": "50-100"
        }
      }
    ],
    "remove_duplicates": false
  }'

JavaScript Example

const API_KEY = 'your_api_key_here';
const BASE_URL = 'https://smtpghost.com/api/v1';

async function scheduleContactsForCampaign(campaignUuid, contacts) {
  const response = await fetch(`${BASE_URL}/campaigns/${campaignUuid}/schedule_contacts`, {
    method: 'POST',
    headers: {
      'X-API-KEY': API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      contacts: contacts,
      remove_duplicates: false
    })
  });
  
  const result = await response.json();
  
  if (result.status === 'success') {
    console.log(`${result.data.contacts_processed.new_contacts_added} contacts scheduled`);
  } else {
    console.error('Error:', result.message);
  }
  
  return result;
}

// Usage with your selected campaign
scheduleContactsForCampaign('your-campaign-uuid', [
  {
    email: 'prospect@example.com',
    first_name: 'John',
    last_name: 'Doe',
    company: 'Target Corp',
    custom_fields: {
      industry: 'Technology',
      company_size: '50-100'
    }
  }
]);

Python Example

import requests

API_KEY = 'your_api_key_here'
BASE_URL = 'https://smtpghost.com/api/v1'

headers = {
    'X-API-KEY': API_KEY,
    'Content-Type': 'application/json'
}

def schedule_contacts_for_campaign(campaign_uuid, contacts):
    data = {
        'contacts': contacts,
        'remove_duplicates': False
    }
    
    response = requests.post(
        f'{BASE_URL}/campaigns/{campaign_uuid}/schedule_contacts',
        headers=headers,
        json=data
    )
    
    result = response.json()
    
    if result['status'] == 'success':
        added = result['data']['contacts_processed']['new_contacts_added']
        print(f'{added} contacts scheduled successfully')
    else:
        print(f'Error: {result["message"]}')
    
    return result

# Usage with your selected campaign
contacts = [
    {
        'email': 'prospect@example.com',
        'first_name': 'John',
        'last_name': 'Doe',
        'company': 'Target Corp',
        'custom_fields': {
            'industry': 'Technology',
            'lead_source': 'Website'
        }
    }
]

schedule_contacts_for_campaign('your-campaign-uuid', contacts)

Error Handling

All error responses follow a consistent format with standard HTTP status codes.

Error Response Format

{
  "status": "error",
  "message": "Error description",
  "errors": ["Detailed error messages"]
}

HTTP Status Codes

200 OK - Request successful
201 Created - Resource created successfully
401 Unauthorized - Invalid or missing API key
404 Not Found - Resource not found
422 Unprocessable Entity - Validation errors
500 Internal Server Error - Server error

Common Error Examples

Invalid API Key (401)

{
  "status": "error",
  "message": "Unauthorized: Invalid API Key or Organization not found"
}

Campaign Not Found (404)

{
  "status": "error",
  "message": "Campaign not found"
}

Non-Evergreen Campaign (422)

{
  "status": "error",
  "message": "Only evergreen campaigns can schedule new contacts. Please create an evergreen campaign.",
  "current_state": "complete",
  "evergreen": false
}

Rate Limits

  • • Maximum 1000 contacts per bulk import request
  • • API rate limits apply based on your plan
  • • Background processing for email scheduling happens asynchronously

Best Practices

  • • Always check the response status before processing data
  • • Handle errors gracefully and provide meaningful feedback to users
  • • Use the scheduled field to confirm if emails were scheduled
  • • Store UUIDs for future API calls rather than internal IDs
  • • Validate email addresses before sending to reduce invalid_emails

Ready to Get Started?

Sign up for an account to get your API key and start building with our platform.