# Author Finder Bulk

## Overview

[Bulk Author Finder](https://tomba.io/bulks/author-finder) helps you discover contact information for content creators, blog authors, and website contributors. Perfect for blogger outreach, content partnerships, and influencer marketing campaigns.

### Key Features

- **Content Author Discovery**: Find contact details for website authors and contributors
- **Blogger Outreach**: Build lists for content marketing and partnership campaigns
- **Influencer Identification**: Discover key voices in your industry
- **Bulk Processing**: Process up to 5,000 URLs in a single operation
- **Contact Enrichment**: Gather comprehensive author profiles and contact methods

### How Author Finder Bulk Works

1. **Identify Target Websites**: Compile URLs of websites, blogs, or content platforms
2. **Upload URL List**: Provide website URLs for author discovery
3. **Process Content**: Analyze websites to identify authors and contributors
4. **Extract Contacts**: Discover contact information for identified authors
5. **Export Results**: Download author contact lists for outreach campaigns

### Limitations

- Each Bulk is limited to 5,000 URLs
- Additional rows will be skipped
- Some special or unexpected characters may be deleted in the file
- Invalid websites won't be imported
- Duplicate URLs won't be imported

## Go SDK Integration

### Installation

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

### Basic Usage

```go
package main

import (
    "fmt"
    "log"
    "strings"
    "time"

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

func main() {
    // Initialize Tomba client
    client := tomba.NewClient("YOUR_TOMBA_KEY", "YOUR_TOMBA_SECRET")

    // Create Author Finder Bulk
    params := &models.BulkCreateParams{
        Name:    "Tech Blog Authors Q1 2024",
        List:    "https://blog.stripe.com\nhttps://blog.shopify.com\nhttps://blog.zoom.us\nhttps://slack.com/blog",
        Sources: true, // Include source information
        Verify:  true, // Verify found emails
    }

    // Create the bulk operation
    response, err := client.CreateBulk(models.BulkTypeAuthor, params)
    if err != nil {
        log.Fatal("Error creating bulk:", err)
    }

    bulkID := *response.Data.ID
    fmt.Printf("Author Finder Bulk created with ID: %d\n", bulkID)

    // Launch the bulk operation
    launchResp, err := client.LaunchBulk(models.BulkTypeAuthor, bulkID)
    if err != nil {
        log.Fatal("Error launching bulk:", err)
    }

    fmt.Printf("Bulk launched: %s\n", launchResp.Data.Message)

    // Monitor progress
    for {
        progress, err := client.GetBulkProgress(models.BulkTypeAuthor, bulkID)
        if err != nil {
            log.Printf("Error getting progress: %v", err)
            break
        }

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

        if progress.Progress >= 100 {
            fmt.Println("Author search completed!")
            break
        }

        time.Sleep(5 * time.Second)
    }

    // Download results
    err = client.SaveBulkResults(models.BulkTypeAuthor, bulkID, "./authors.csv", "full")
    if err != nil {
        log.Printf("Error downloading results: %v", err)
    } else {
        fmt.Println("Results saved to ./authors.csv")
    }
}
```

### Advanced Management

```go
func manageAuthorBulks(client *tomba.Tomba) {
    // Get all author finder bulks with pagination
    allBulks, err := client.GetAllAuthorBulks(&models.BulkGetParams{
        Page:      1,
        Limit:     10,
        Direction: "desc",
        Filter:    "all",
    })
    if err != nil {
        log.Printf("Error getting bulks: %v", err)
        return
    }

    fmt.Printf("Total author bulks: %d\n", allBulks.Meta.Total)

    // Process each bulk
    for _, bulk := range allBulks.Data {
        fmt.Printf("Bulk: %s (ID: %d) - Progress: %d%%\n",
            bulk.Name, bulk.BulkID, bulk.Progress)

        // Download completed bulks
        if bulk.Progress >= 100 && !bulk.Used {
            err := client.SaveBulkResults(
                models.BulkTypeAuthor,
                bulk.BulkID,
                fmt.Sprintf("./author_results_%d.csv", bulk.BulkID),
                "full",
            )
            if err == nil {
                fmt.Printf("Downloaded results for bulk %d\n", bulk.BulkID)
            }
        }
    }

    // Rename a bulk
    bulkID := int64(12345) // Replace with actual ID
    renameParams := &models.BulkRenameParams{
        Name: "Updated Author Campaign 2024",
    }

    _, err = client.RenameBulk(models.BulkTypeAuthor, bulkID, renameParams)
    if err == nil {
        fmt.Println("Bulk renamed successfully")
    }

    // Archive old bulks
    _, err = client.ArchiveBulk(models.BulkTypeAuthor, bulkID)
    if err == nil {
        fmt.Println("Bulk archived successfully")
    }
}
```

### Error Handling and Best Practices

```go
func robustAuthorSearch(client *tomba.Tomba, urls []string) error {
    // Validate input
    if len(urls) == 0 {
        return fmt.Errorf("no URLs provided")
    }

    if len(urls) > 5000 {
        return fmt.Errorf("too many URLs: %d (max: 5000)", len(urls))
    }

    // Create URL list
    urlList := strings.Join(urls, "\n")

    params := &models.BulkCreateParams{
        Name:    fmt.Sprintf("Author Search %s", time.Now().Format("2006-01-02")),
        List:    urlList,
        Sources: true,
        Verify:  true,
    }

    // Create with retry logic
    var response *models.BulkCreateResponse
    var err error

    for i := 0; i < 3; i++ {
        response, err = client.CreateBulk(models.BulkTypeAuthor, params)
        if err == nil {
            break
        }

        log.Printf("Attempt %d failed: %v", i+1, err)
        time.Sleep(time.Duration(i+1) * time.Second)
    }

    if err != nil {
        return fmt.Errorf("failed to create bulk after retries: %w", err)
    }

    // Launch and monitor
    bulkID := *response.Data.ID

    _, err = client.LaunchBulk(models.BulkTypeAuthor, bulkID)
    if err != nil {
        return fmt.Errorf("failed to launch bulk: %w", err)
    }

    // Wait for completion with timeout
    timeout := time.After(30 * time.Minute)
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-timeout:
            return fmt.Errorf("bulk operation timed out")

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

            fmt.Printf("Progress: %d%%\n", progress.Progress)

            if progress.Progress >= 100 {
                fmt.Println("Author search completed successfully!")
                return nil
            }
        }
    }
}
```

### Input Format Examples

**Text List (most common):**

```
https://blog.stripe.com
https://blog.shopify.com
https://blog.zoom.us
https://slack.com/blog
https://blog.dropbox.com
```

**CSV File Upload:**

```text
URL
https://blog.stripe.com
https://blog.shopify.com
https://blog.zoom.us
https://slack.com/blog
```

### Best Practices

- **Quality URLs**: Use complete, accessible blog and website URLs
- **Verify Results**: Enable email verification for higher quality contacts
- **Descriptive Names**: Use clear, descriptive names for easy identification
- **Monitor Progress**: Check progress regularly for large operations
- **Clean Results**: Review and clean results before outreach campaigns
- **Respect Limits**: Stay within the 5,000 URL limit per bulk
- **Regular Maintenance**: Archive completed bulks to keep dashboard organized

### Common Use Cases

- **Blogger Outreach**: Find authors for guest posting opportunities
- **Influencer Marketing**: Identify key content creators in your niche
- **Partnership Development**: Connect with thought leaders and industry experts
- **Content Collaboration**: Build relationships with complementary brands
- **Media Relations**: Find journalists and bloggers covering your industry
