Skip to main content

CSV Upload Guide

Upload and validate CSV files containing up to 10,000 emails through the GTMAPIs web dashboard.

Prerequisites

Before uploading a CSV:

Step-by-Step Process

1

Prepare Your CSV

Your CSV must contain an email column. Other columns are preserved in the output.
2

Upload to Dashboard

Go to Dashboard and click “Upload CSV”
3

Map Email Column

Select which column contains email addresses
4

Start Validation

Click “Validate” to begin processing
5

Receive Results

Download CSV with validation results or receive via email

CSV Format Requirements

Supported Formats

  • File type: .csv only
  • Max size: 10,000 emails per upload
  • Encoding: UTF-8 recommended
  • Delimiter: Comma (,)

Example Input CSV

email,first_name,last_name,company
john@company.com,John,Doe,Acme Inc
info@company.com,Info,Team,Acme Inc
invalid@notreal.com,Invalid,User,Fake Corp
sarah@startup.io,Sarah,Williams,Startup LLC

Column Mapping

During upload, you’ll select which column contains emails:
  • Preview shows first 5 rows
  • Click on the column header to select
  • System validates format before processing

Output Format

Added Validation Columns

Your output CSV includes 8 new columns with validation results:
ColumnDescriptionExample Values
resultValidation resultvalid, valid_role_based, risky, invalid, unknown
reasonExplanation of result”Valid personal email”, “Role-based email address”
is_role_basedRole-based flagtrue, false
is_free_providerFree provider flagtrue, false
is_catch_allCatch-all flagtrue, false
b2b_outbound_qualityB2B quality scorehigh, low, none
credits_chargedCredits used0, 1
charge_reasonCharge explanation”Valid personal email with high B2B outbound quality”

Example Output CSV

email,first_name,last_name,company,result,reason,is_role_based,is_free_provider,is_catch_all,b2b_outbound_quality,credits_charged,charge_reason
john@company.com,John,Doe,Acme Inc,valid,,false,false,false,high,1,"Valid personal email with high B2B outbound quality"
info@company.com,Info,Team,Acme Inc,valid_role_based,"Role-based email address",true,false,false,low,0,"Role-based email - free for low B2B value"
invalid@notreal.com,Invalid,User,Fake Corp,invalid,"Domain has no MX records",false,false,false,none,0,"Invalid email does not consume credits"
sarah@startup.io,Sarah,Williams,Startup LLC,valid,,false,false,false,high,1,"Valid personal email with high B2B outbound quality"

Processing Details

Batch Processing

The system processes emails in batches:
  1. Deduplication: Removes duplicate emails (case-insensitive)
  2. Batch size: 100 emails per API request
  3. Delay: 500ms between batches to avoid rate limits
  4. Retry logic: Automatic retry on transient failures

Processing Time

EmailsEstimated Time
100~10 seconds
1,000~1-2 minutes
5,000~5-10 minutes
10,000~10-20 minutes
Note: Times vary based on DNS caching and SMTP responsiveness

Email Notification

When validation completes:
  • Email sent to your account address
  • CSV file attached
  • Summary statistics included
  • Notification usually arrives within 1 minute

Credit Usage

How Credits Are Deducted

Credits are deducted AFTER validation:
  1. Upload CSV (no charge yet)
  2. System validates all emails
  3. Counts high-quality results
  4. Deducts only for result: "valid" + b2b_outbound_quality: "high"
  5. Returns results

Example Credit Usage

Uploaded CSV: 1,000 emails
ResultCountCredits
Valid personal400400
Role-based2500 (FREE)
Catch-all2000 (FREE)
Invalid1500 (FREE)
Total1,000400
**Cost at 0.001/credit:0.001/credit**: 0.40 (60% savings vs charging for all)

Insufficient Credits

If you don’t have enough credits:
  • Upload is rejected before processing
  • Error message shows required vs available
  • No partial processing (all-or-nothing)
Solution: Purchase more credits or reduce batch size

Best Practices

Before Uploading

Remove obvious invalids and duplicates locally:
// Remove duplicates
const unique = [...new Set(emails.map(e => e.toLowerCase()))];

// Basic syntax filter
const validFormat = unique.filter(e => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e));

// Remove common placeholders
const cleaned = validFormat.filter(e =>
  !e.includes('example.com') &&
  !e.includes('test@') &&
  !e.includes('@test.')
);
Check your CSV before uploading:
  • Proper comma delimiters
  • No missing headers
  • Consistent column count per row
  • UTF-8 encoding (especially for international names)
  • No BOM (Byte Order Mark) issues
Before processing 10,000 emails:
  1. Extract first 100 rows
  2. Upload test batch
  3. Review results quality
  4. Adjust source or filters if needed
  5. Process full list

After Validation

Separate emails by B2B quality:
// High quality for primary campaigns
const highQuality = results.filter(r =>
  r.result === 'valid' && r.b2b_outbound_quality === 'high'
);

// Role-based for secondary outreach
const roleBased = results.filter(r =>
  r.result === 'valid_role_based'
);

// Remove these completely
const toRemove = results.filter(r =>
  r.result === 'invalid' || r.result === 'risky'
);
Keep records of validations:
  • Original upload date
  • Source of emails
  • Validation results summary
  • Credits consumed
  • Campaign performance per quality tier
Re-validate lists every 3-6 months as emails change
Compare campaign performance by quality:
High Quality Emails:
- Open rate: 25-35%
- Response rate: 5-10%
- Cost per lead: $X

Role-Based Emails:
- Open rate: 10-15%
- Response rate: 1-2%
- Cost per lead: $X * 5

Risky/Invalid:
- Bounce rate: 50-80%
- Damages sender reputation
- Cost: Wasted credits

Common Issues

CSV Upload Fails

Problem: “Invalid CSV format” error Causes:
  • Non-UTF-8 encoding
  • Inconsistent column counts
  • Missing headers
  • Special characters in data
Solution:
  1. Open CSV in text editor
  2. Check for encoding issues
  3. Verify all rows have same column count
  4. Remove special characters or escape properly

No Email Column Detected

Problem: System can’t find email column Causes:
  • Column named something other than “email”
  • Emails in wrong format
  • Empty column
Solution:
  1. Rename column to “email” (lowercase)
  2. Verify emails are in user@domain.com format
  3. Check first few rows have valid data

Processing Takes Too Long

Problem: Validation stuck or timing out Causes:
  • Large batch size
  • Many slow SMTP servers
  • Network issues
Solution:
  1. Check processing status in dashboard
  2. Wait for email notification
  3. If > 30 minutes, contact support
  4. Try smaller batches (< 5,000 emails)

Unexpected Credit Charges

Problem: Charged more credits than expected Causes:
  • Misunderstanding of credit system
  • More high-quality emails than estimated
  • Duplicates not removed
Solution:
  1. Check CSV for duplicate emails
  2. Review validation results
  3. Filter by credits_charged column
  4. Only valid + b2b_outbound_quality: "high" are charged

Programmatic CSV Validation

For automation, use the API directly:
const fs = require('fs');
const Papa = require('papaparse');

async function validateCSV(filePath) {
  // 1. Read CSV
  const csvData = fs.readFileSync(filePath, 'utf8');
  const parsed = Papa.parse(csvData, { header: true });

  // 2. Extract emails
  const emails = parsed.data.map(row => row.email).filter(Boolean);

  // 3. Validate in batches
  const results = [];
  for (let i = 0; i < emails.length; i += 100) {
    const batch = emails.slice(i, i + 100);

    const response = await fetch('https://api.gtmapis.com/v1/validate/bulk', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': process.env.GTMAPIS_API_KEY
      },
      body: JSON.stringify({ emails: batch })
    });

    const batchResults = await response.json();
    results.push(...batchResults.results);

    // Rate limit delay
    await new Promise(resolve => setTimeout(resolve, 500));
  }

  // 4. Merge results with original data
  const merged = parsed.data.map(row => {
    const result = results.find(r => r.email === row.email);
    return { ...row, ...result };
  });

  // 5. Write output CSV
  const csv = Papa.unparse(merged);
  fs.writeFileSync('output.csv', csv);

  console.log(`Validated ${results.length} emails`);
  return merged;
}

Next Steps