# Email Finder Bulk

## Overview

[Bulk Email Finder](https://tomba.io/bulks/email-finder) enables you to discover email addresses by combining names with company domains. Upload a CSV file containing first names, last names, and company domains to generate accurate email addresses for your prospects.

### Key Features

- **Name-to-Email Matching**: Convert names and domains into valid email addresses
- **CSV Upload Support**: Process large lists via file upload
- **Pattern Recognition**: Use advanced algorithms to determine email patterns
- **Flexible Input**: Support for various name and domain combinations
- **High Accuracy**: Leverage domain-specific email patterns for precision
- **Verification Options**: Optional email verification for deliverability

### How Email Finder Bulk Works

1. **Prepare CSV File**: Create a file with columns for first name, last name, and domain
2. **Map Columns**: Specify which columns contain the required data
3. **Upload and Configure**: Upload your file and set processing parameters
4. **Process Emails**: Launch the email finding algorithm
5. **Download Results**: Export discovered email addresses

### Limitations

- You can find up to 10,000 email addresses per file
- Additional rows will be skipped
- Duplicate rows will be skipped
- Some special or unexpected characters may be deleted in the file
- Invalid websites won't be imported

## Go SDK Integration

### Installation

```bash
go get github.com/tomba-io/go
```

#### Basic Setup

```go
package main

import (
    "fmt"
    "log"
    "time"

    "github.com/tomba-io/go/tomba"
    "github.com/tomba-io/go/tomba/models"
)

func main() {
    // Initialize Tomba client
    client := tomba.NewTomba("your-api-key", "your-secret-key")

    // Your email finder code here
}
```

### Creating Email Finder Bulk from CSV

```go
// Create finder bulk with CSV file containing names and domains
params := &models.BulkFinderCreateParams{
    BulkCreateParams: models.BulkCreateParams{
        Name:      "Lead Generation - Tech Companies Q1",
        Delimiter: ",",
        Verify:    true,  // Enable email verification
    },
    BulkFinderParams: models.BulkFinderParams{
        ColumnFirst:  1,  // First name column (1-based)
        ColumnLast:   2,  // Last name column
        ColumnDomain: 3,  // Company domain column
        Skip:         false,
    },
}

// Upload CSV and create bulk finder operation
response, err := client.CreateFinderBulk(params, "/path/to/prospects.csv")
if err != nil {
    log.Fatal("Failed to create finder bulk:", err)
}

bulkID := *response.Data.ID
fmt.Printf("Created finder bulk with ID: %d\n", bulkID)
```

#### Alternative Column Mapping (Full Name + Domain)

```go
// When you have full name in single column
params := &models.BulkFinderCreateParams{
    BulkCreateParams: models.BulkCreateParams{
        Name:      "Sales Prospects - SaaS Companies",
        Delimiter: ",",
        Verify:    true,
    },
    BulkFinderParams: models.BulkFinderParams{
        ColumnName:   1,  // Full name column
        ColumnDomain: 2,  // Domain column
        Skip:         false,
    },
}

response, err := client.CreateFinderBulk(params, "prospects-with-fullnames.csv")
if err != nil {
    log.Fatal("Failed to create finder bulk:", err)
}
```

### Launching and Monitoring Email Finding

```go
// Launch the email finding process
launchResponse, err := client.LaunchBulk(models.BulkTypeFinder, bulkID)
if err != nil {
    log.Fatal("Failed to launch finder bulk:", err)
}

fmt.Println("Email finding process started!")

// Monitor progress with detailed status
for {
    progress, err := client.GetBulkProgress(models.BulkTypeFinder, bulkID)
    if err != nil {
        log.Printf("Error checking progress: %v", err)
        time.Sleep(30 * time.Second)
        continue
    }

    fmt.Printf("Finding emails: %d%% complete (%d processed)\n",
        progress.Progress,
        progress.Processed,
    )

    if progress.Status {
        fmt.Println("Email finding completed successfully!")
        break
    }

    // Check every 30 seconds
    time.Sleep(30 * time.Second)
}
```

### Retrieving Found Emails

```go
// Get detailed bulk information
bulk, err := client.GetBulk(models.BulkTypeFinder, bulkID)
if err != nil {
    log.Fatal("Failed to get bulk details:", err)
}

bulkInfo := bulk.Data[0]
fmt.Printf("Bulk: %s\n", bulkInfo.Name)
fmt.Printf("Status: %v\n", bulkInfo.Status)
fmt.Printf("Total Processed: %d\n", bulkInfo.Processed)
fmt.Printf("Emails Found: %d\n", *bulkInfo.TotalEmails)

// Download all found emails
err = client.SaveBulkResults(
    models.BulkTypeFinder,
    bulkID,
    "found-emails.csv",
    "full",
)
if err != nil {
    log.Fatal("Failed to download results:", err)
}

fmt.Println("Found emails saved to found-emails.csv")
```

### Downloading Different Result Types

```go
// Download only valid emails
err = client.SaveBulkResults(
    models.BulkTypeFinder,
    bulkID,
    "valid-emails.csv",
    "valid",
)
if err != nil {
    log.Printf("Error downloading valid emails: %v", err)
}

// Download records where no emails were found
err = client.SaveBulkResults(
    models.BulkTypeFinder,
    bulkID,
    "not-found.csv",
    "not_found",
)
if err != nil {
    log.Printf("Error downloading not found records: %v", err)
}

fmt.Println("Results downloaded in separate files")
```

### Managing Finder Bulk Operations

```go
// List all finder bulks with filtering
params := &models.BulkGetParams{
    Page:      1,
    Limit:     10,
    Direction: "desc",
    Filter:    "all", // or "archived"
}

bulks, err := client.GetAllFinderBulks(params)
if err != nil {
    log.Fatal("Failed to get finder bulks:", err)
}

fmt.Printf("Found %d finder bulk operations:\n", len(bulks.Data))
for _, bulk := range bulks.Data {
    fmt.Printf("- ID: %d, Name: %s, Progress: %d%%\n",
        bulk.BulkID, bulk.Name, bulk.Progress)
}

// Rename a bulk operation
renameParams := &models.BulkRenameParams{
    Name: "Updated Lead Generation Campaign",
}

_, err = client.RenameBulk(models.BulkTypeFinder, bulkID, renameParams)
if err != nil {
    log.Fatal("Failed to rename bulk:", err)
}

// Archive completed bulk
_, err = client.ArchiveBulk(models.BulkTypeFinder, bulkID)
if err != nil {
    log.Fatal("Failed to archive bulk:", err)
}
```

### Advanced Example: Complete Email Finding Workflow

```go
func findEmailsFromProspects(client *tomba.Tomba, csvPath string) error {
    // Step 1: Create finder bulk with verification
    params := &models.BulkFinderCreateParams{
        BulkCreateParams: models.BulkCreateParams{
            Name:      fmt.Sprintf("Email Discovery - %s", time.Now().Format("2006-01-02")),
            Delimiter: ",",
            Verify:    true, // Verify found emails
        },
        BulkFinderParams: models.BulkFinderParams{
            ColumnFirst:  1, // First name in column 1
            ColumnLast:   2, // Last name in column 2
            ColumnDomain: 3, // Domain in column 3
            Skip:         false,
        },
    }

    response, err := client.CreateFinderBulk(params, csvPath)
    if err != nil {
        return fmt.Errorf("failed to create finder bulk: %w", err)
    }

    bulkID := *response.Data.ID
    log.Printf("Created finder bulk: %d", bulkID)

    // Step 2: Launch the finding process
    _, err = client.LaunchBulk(models.BulkTypeFinder, bulkID)
    if err != nil {
        return fmt.Errorf("failed to launch bulk: %w", err)
    }

    log.Println("Email finding started...")

    // Step 3: Monitor with progress reporting
    startTime := time.Now()
    timeout := time.After(45 * time.Minute) // Longer timeout for finding
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()

    var lastProgress int

    for {
        select {
        case <-timeout:
            return fmt.Errorf("email finding timed out after 45 minutes")

        case <-ticker.C:
            progress, err := client.GetBulkProgress(models.BulkTypeFinder, bulkID)
            if err != nil {
                log.Printf("Error checking progress: %v", err)
                continue
            }

            // Only log if progress changed significantly
            if progress.Progress != lastProgress {
                elapsed := time.Since(startTime).Round(time.Second)
                log.Printf("Finding progress: %d%% (%d processed) - Elapsed: %v",
                    progress.Progress, progress.Processed, elapsed)
                lastProgress = progress.Progress
            }

            if progress.Status {
                // Step 4: Get final results and download
                bulk, err := client.GetBulk(models.BulkTypeFinder, bulkID)
                if err != nil {
                    return fmt.Errorf("failed to get final bulk details: %w", err)
                }

                info := bulk.Data[0]
                log.Printf("Email finding completed!")
                log.Printf("Total processed: %d", info.Processed)
                if info.TotalEmails != nil {
                    log.Printf("Emails found: %d", *info.TotalEmails)
                }

                // Download results
                outputFile := fmt.Sprintf("found-emails-%d.csv", bulkID)
                err = client.SaveBulkResults(models.BulkTypeFinder, bulkID, outputFile, "valid")
                if err != nil {
                    return fmt.Errorf("failed to save results: %w", err)
                }

                log.Printf("Valid emails saved to: %s", outputFile)
                return nil
            }
        }
    }
}

// Usage example
func main() {
    client := tomba.NewTomba("your-api-key", "your-secret-key")

    err := findEmailsFromProspects(client, "prospects.csv")
    if err != nil {
        log.Fatal("Email finding failed:", err)
    }
}
```

### CSV File Format Examples

**Example 1: Separate Name Columns**

```text
first_name,last_name,company_domain
John,Doe,example.com
Jane,Smith,techcorp.com
Michael,Johnson,startup.io
```

**Example 2: Full Name Column**

```text
full_name,company_domain
John Doe,example.com
Jane Smith,techcorp.com
Michael Johnson,startup.io
```

**Example 3: With Additional Data**

```text
first_name,last_name,company_domain,title,company_name
John,Doe,example.com,CEO,Example Corp
Jane,Smith,techcorp.com,CTO,TechCorp Inc
Michael,Johnson,startup.io,Founder,Startup IO
```

## Best Practices

- **Accurate Name Data**: Ensure names are properly formatted and spelled correctly
- **Domain Verification**: Verify company domains before processing
- **Column Mapping**: Carefully map CSV columns to the correct data fields
- **File Organization**: Use clear column headers and consistent formatting
- **Result Validation**: Verify a sample of results before using for outreach
- **Batch Processing**: Process large lists in manageable batches
- **Verification**: Enable email verification to ensure deliverability
- **Progress Monitoring**: Monitor long-running operations for completion
- **Error Handling**: Implement robust error handling for production use
- **Data Quality**: Clean and validate input data for better results
