list_campaigns
Retrieve and filter email campaigns from Instantly API with automatic pagination support to access all campaigns efficiently.
Instructions
List campaigns with optional filters and complete pagination support. COMPLETE PAGINATION: To get ALL campaigns, use one of these approaches:
Set limit=100 or higher (automatically triggers complete pagination)
Set get_all=true (forces complete pagination)
Use limit="all" (string triggers complete pagination)
PAGINATION ALGORITHM: When requesting all campaigns, the tool will automatically:
Start with limit=100 per page
Continue fetching until next_starting_after is null or empty results
Report progress: "Retrieved 100... 200... 304 total campaigns"
Return summarized data to prevent size limits
Use get_campaign for full details of specific campaigns
FILTERS: search and status filters work with both single-page and complete pagination modes.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| get_all | No | Set to true to force complete pagination and retrieve ALL campaigns regardless of limit setting. | |
| limit | No | Number of campaigns to return (1-100, default: 20). Use limit=100+ or limit="all" to trigger complete pagination that retrieves ALL campaigns automatically. | |
| search | No | Search term to filter campaigns by name (works with complete pagination) | |
| starting_after | No | ID of the last item from previous page for manual pagination. Not needed when using complete pagination (limit=100+). | |
| status | No | Filter by campaign status (works with complete pagination) |
Implementation Reference
- src/handlers/tool-executor.ts:153-239 (handler)Main handler execution logic for list_campaigns tool. Makes API call to /campaigns, handles pagination, maps numeric status codes to human-readable labels, adds metadata.case 'list_campaigns': { console.error('[Instantly MCP] 📋 Executing list_campaigns (sequential pagination)...'); try { const startTime = Date.now(); // Status mapping: API returns numbers, we convert to human-readable labels const STATUS_MAP: Record<number, string> = { 0: 'Draft', 1: 'Active', 2: 'Paused', 3: 'Completed', 4: 'Running Subsequences', '-99': 'Account Suspended', '-1': 'Accounts Unhealthy', '-2': 'Bounce Protect' }; // Build query parameters for single page request const queryParams: any = { limit: args?.limit || 100, // Default to 100 items per page (max pagination) }; // Add cursor if provided (for subsequent pages) if (args?.starting_after) { queryParams.starting_after = args.starting_after; console.error(`[Instantly MCP] 📄 Fetching page with cursor: ${args.starting_after}`); } else { console.error('[Instantly MCP] 📄 Fetching first page'); } // Add API filter parameters if (args?.search) queryParams.search = args.search; if (args?.tag_ids) queryParams.tag_ids = args.tag_ids; // Make single API call to /campaigns endpoint const response = await makeInstantlyRequest('/campaigns', { method: 'GET', params: queryParams }, apiKey); const elapsed = Date.now() - startTime; // Extract data and pagination info from response const data = Array.isArray(response) ? response : (response.items || response.data || []); const nextCursor = response.next_starting_after || null; const hasMore = !!nextCursor; console.error(`[Instantly MCP] ✅ Retrieved ${data.length} campaigns in ${elapsed}ms (has_more: ${hasMore})`); // Apply status mapping to all campaigns (convert numeric status to human-readable labels) const campaignsWithReadableStatus = data.map((campaign: any) => ({ ...campaign, status_label: STATUS_MAP[campaign.status] || `Unknown (${campaign.status})`, status_code: campaign.status // Keep original numeric code for reference })); // Track applied filters const filtersApplied: any = {}; if (args?.search) filtersApplied.search = args.search; if (args?.tag_ids) filtersApplied.tag_ids = args.tag_ids; // Return single page with clear pagination metadata return createMCPResponse({ data: campaignsWithReadableStatus, pagination: { returned_count: campaignsWithReadableStatus.length, has_more: hasMore, next_starting_after: nextCursor, limit: queryParams.limit, current_page_note: hasMore ? `Retrieved ${campaignsWithReadableStatus.length} campaigns. More results available. To get next page, call list_campaigns again with starting_after='${nextCursor}'` : `Retrieved all available campaigns (${campaignsWithReadableStatus.length} items).` }, filters_applied: Object.keys(filtersApplied).length > 0 ? filtersApplied : undefined, metadata: { request_time_ms: elapsed, success: true, status_mapping_note: 'All campaigns include status_label (human-readable) and status_code (numeric) fields' }, success: true }); } catch (error: any) { console.error('[Instantly MCP] ❌ Error in list_campaigns:', error.message); throw error; } }
- src/tools/campaign-tools.ts:42-56 (schema)MCP tool definition and input schema for list_campaigns, including properties for pagination and filtering.{ name: 'list_campaigns', title: 'List Campaigns', description: 'List campaigns with pagination. Filter by name search or tags.', annotations: { readOnlyHint: true }, inputSchema: { type: 'object', properties: { limit: { type: 'number', description: '1-100, default: 100' }, starting_after: { type: 'string', description: 'Cursor from pagination.next_starting_after' }, search: { type: 'string', description: 'Search by campaign NAME only (not status)' }, tag_ids: { type: 'string', description: 'Comma-separated tag IDs' } } } },
- src/tools/index.ts:49-56 (registration)Registration of campaignTools (containing list_campaigns) into the main TOOLS_DEFINITION array used by MCP server.return [ ...accountTools, ...campaignTools, ...leadTools, ...emailTools, ...analyticsTools, ]; }
- src/validation.ts:283-292 (schema)Zod validation schema ListCampaignsSchema for input parameters and associated validator function.* List campaigns validation schema */ export const ListCampaignsSchema = z.object({ limit: PaginationLimitSchema, // Already optional in the schema definition starting_after: z.string().optional(), get_all: z.boolean().optional(), search: z.string().optional(), status: z.string().optional() });