checkboxHQ + Webflow + Airtable Integration Guide

checkboxHQ Team
checkboxHQ airtable webflow forms integration email verification block disposable emails on webflow webflow airtable integration

checkboxHQ + Webflow + Airtable Integration Guide

Overview

This guide demonstrates how to integrate checkboxHQ’s email verification API with Webflow forms and store validated results in Airtable using a non-blocking verification approach. checkboxHQ validates emails asynchronously after form submission, ensuring a smooth user experience without delays or interruptions.

This setup is ideal for:

  • Lead capture forms that need post-submission email quality scoring
  • Newsletter signups with background verification for list hygiene
  • Contact forms that store all submissions but flag invalid emails for review
  • Any workflow requiring clean email data without impacting form conversion rates

Architecture

Webflow Form Submission → Immediate Success Response to User
Webhook (async) → Serverless Function
checkboxHQ API Verification (background)
Airtable Storage (with verification results)

Key Principle: The user receives immediate confirmation of their submission. Email verification happens in the background without blocking the user’s experience.

Prerequisites

  1. checkboxHQ Account

  2. Webflow

    • Published site with form
    • Webflow site plan (required for webhooks)
  3. Airtable

    • Airtable account with a base created
    • API key or Personal Access Token
  4. Serverless Platform (choose one)

    • Netlify Functions (recommended for beginners)
    • Vercel Functions
    • AWS Lambda
    • Cloudflare Workers

Step 1: Set Up Airtable Base

Create Your Base Structure

Create a table called Email Submissions with these fields:

Field NameField TypeDescription
Submission IDSingle line textUnique identifier for tracking
EmailEmailThe submitted email address
Form SourceSingle line textWhich Webflow form submitted
Submission DateCreated timeAuto-populated timestamp
StatusSingle selectOptions: Valid, Disposable Email, Invalid Domain, Verification Error, Pending Verification
Verification StatusSingle selectOptions: Pending, In Progress, Completed, Failed
Is DisposableCheckboxWhether email is from disposable provider
Is Public ProviderCheckboxWhether email is from public provider (Gmail, etc.)
Has MX RecordCheckboxWhether domain has valid MX records
Has A RecordCheckboxWhether domain has valid A records
DomainSingle line textExtracted domain from email
ProviderSingle line textEmail provider name if identified
Verified AtDate & timeWhen verification completed
Verification ErrorLong textError message if verification failed

Why this structure?

  • Initial submission creates record immediately with “Pending Verification” status
  • Background verification updates the same record with results
  • You can filter and view submissions while verification is in progress
  • No user waits for verification to complete

Get Airtable Credentials

  1. Go to airtable.com/create/tokens
  2. Create a Personal Access Token with these scopes:
    • data.records:read
    • data.records:write
  3. Note your:
    • Base ID: Found in API documentation (starts with app)
    • Table Name: Email Submissions (or your custom name)
    • Access Token: Your newly created token

Step 2: Create Serverless Function

Example: Netlify Function (Non-Blocking Pattern)

Create a file: netlify/functions/verify-email.js

netlify/functions/verify-email.js
const fetch = require('node-fetch');
exports.handler = async (event, context) => {
// Only allow POST requests
if (event.httpMethod !== 'POST') {
return {
statusCode: 405,
body: JSON.stringify({ error: 'Method not allowed' })
};
}
try {
// Parse Webflow webhook payload
const payload = JSON.parse(event.body);
const email = payload.data?.email || payload.email;
const formName = payload.name || 'Unknown Form';
const submissionId = payload._id || Date.now().toString();
if (!email) {
return {
statusCode: 400,
body: JSON.stringify({ error: 'Email is required' })
};
}
// STEP 1: Immediately store submission in Airtable (user already received success)
await storeInitialSubmission(email, formName, submissionId);
// STEP 2: Perform verification asynchronously (fire and forget)
// Don't await - let it run in background
verifyAndUpdateAsync(email, submissionId).catch(error => {
console.error('Background verification error:', error);
// Log error but don't fail the webhook response
});
// STEP 3: Return immediate success to Webflow
// User experience is not impacted by verification delay
return {
statusCode: 200,
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
success: true,
message: 'Submission received and being processed',
submissionId: submissionId
})
};
} catch (error) {
console.error('Error:', error);
return {
statusCode: 500,
body: JSON.stringify({
error: 'Submission processing failed',
details: error.message
})
};
}
};
// Store initial submission immediately (before verification)
async function storeInitialSubmission(email, formName, submissionId) {
const AIRTABLE_TOKEN = process.env.AIRTABLE_TOKEN;
const AIRTABLE_BASE_ID = process.env.AIRTABLE_BASE_ID;
const AIRTABLE_TABLE_NAME = process.env.AIRTABLE_TABLE_NAME || 'Email Submissions';
const airtablePayload = {
records: [
{
fields: {
'Email': email,
'Form Source': formName,
'Submission ID': submissionId,
'Status': 'Pending Verification',
'Verification Status': 'In Progress'
}
}
]
};
const response = await fetch(
`https://api.airtable.com/v0/${AIRTABLE_BASE_ID}/${encodeURIComponent(AIRTABLE_TABLE_NAME)}`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${AIRTABLE_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(airtablePayload)
}
);
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Airtable error: ${errorText}`);
}
const result = await response.json();
return result.records[0].id; // Return Airtable record ID
}
// Async verification that updates the existing record
async function verifyAndUpdateAsync(email, submissionId) {
try {
// Perform CheckBoxHQ verification
const verificationData = await verifyEmailWithCheckBox(email);
// Update the existing Airtable record with verification results
await updateAirtableRecord(submissionId, verificationData);
console.log(`Verification completed for ${email}`);
} catch (error) {
console.error(`Verification failed for ${email}:`, error);
// Update record with error status
await updateAirtableRecord(submissionId, {
verification_error: error.message,
verification_status: 'Failed'
});
}
}
// CheckBoxHQ API Integration Functions
async function verifyEmailWithCheckBox(email) {
const CHECKBOX_API_KEY = process.env.CHECKBOX_API_KEY;
// Perform all verifications (adjust based on your needs)
// 1. Disposable email check (1 credit)
const disposableCheck = await fetch('https://api.checkboxhq.com/api/v1/verify_disposable/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': CHECKBOX_API_KEY
},
body: JSON.stringify({ email })
});
if (!disposableCheck.ok) {
throw new Error(`Disposable check failed: ${disposableCheck.statusText}`);
}
const disposableData = await disposableCheck.json();
// 2. DNS verification (2 credits)
const dnsCheck = await fetch('https://api.checkboxhq.com/api/v1/verify_dns/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': CHECKBOX_API_KEY
},
body: JSON.stringify({ email })
});
if (!dnsCheck.ok) {
throw new Error(`DNS check failed: ${dnsCheck.statusText}`);
}
const dnsData = await dnsCheck.json();
// 3. Public provider check (1 credit)
const publicCheck = await fetch('https://api.checkboxhq.com/api/v1/verify_public_provider/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': CHECKBOX_API_KEY
},
body: JSON.stringify({ email })
});
if (!publicCheck.ok) {
throw new Error(`Public provider check failed: ${publicCheck.statusText}`);
}
const publicData = await publicCheck.json();
// Combine all verification results
return {
email: disposableData.email,
is_disposable: disposableData.is_disposable,
is_valid_format: disposableData.is_valid_format,
domain: disposableData.domain,
provider: disposableData.provider || dnsData.provider || publicData.provider,
has_mx_record: dnsData.has_mx_record,
has_a_record: dnsData.has_a_record,
mx_records: dnsData.mx_records,
a_records: dnsData.a_records,
is_public_provider: publicData.is_public_provider
};
}
async function updateAirtableRecord(submissionId, verificationData) {
const AIRTABLE_TOKEN = process.env.AIRTABLE_TOKEN;
const AIRTABLE_BASE_ID = process.env.AIRTABLE_BASE_ID;
const AIRTABLE_TABLE_NAME = process.env.AIRTABLE_TABLE_NAME || 'Email Submissions';
// First, find the record by Submission ID
const searchUrl = `https://api.airtable.com/v0/${AIRTABLE_BASE_ID}/${encodeURIComponent(AIRTABLE_TABLE_NAME)}?filterByFormula={Submission ID}="${submissionId}"`;
const searchResponse = await fetch(searchUrl, {
headers: {
'Authorization': `Bearer ${AIRTABLE_TOKEN}`
}
});
const searchData = await searchResponse.json();
if (!searchData.records || searchData.records.length === 0) {
throw new Error(`Record not found for submission ID: ${submissionId}`);
}
const recordId = searchData.records[0].id;
// Determine final status
let status = 'Valid';
if (verificationData.verification_error) {
status = 'Verification Error';
} else if (verificationData.is_disposable) {
status = 'Disposable Email';
} else if (!verificationData.has_mx_record) {
status = 'Invalid Domain';
}
// Update the record with verification results
const updatePayload = {
fields: {
'Is Disposable': verificationData.is_disposable || false,
'Is Public Provider': verificationData.is_public_provider || false,
'Has MX Record': verificationData.has_mx_record || false,
'Has A Record': verificationData.has_a_record || false,
'Domain': verificationData.domain || '',
'Provider': verificationData.provider || '',
'Status': status,
'Verification Status': verificationData.verification_error ? 'Failed' : 'Completed',
'Verified At': new Date().toISOString()
}
};
if (verificationData.verification_error) {
updatePayload.fields['Verification Error'] = verificationData.verification_error;
}
const updateResponse = await fetch(
`https://api.airtable.com/v0/${AIRTABLE_BASE_ID}/${encodeURIComponent(AIRTABLE_TABLE_NAME)}/${recordId}`,
{
method: 'PATCH',
headers: {
'Authorization': `Bearer ${AIRTABLE_TOKEN}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(updatePayload)
}
);
if (!updateResponse.ok) {
const errorText = await updateResponse.text();
throw new Error(`Airtable update error: ${errorText}`);
}
return await updateResponse.json();
}

Environment Variables

Add these to your Netlify dashboard (or equivalent):

CHECKBOX_API_KEY=your_checkbox_api_key_here
AIRTABLE_TOKEN=your_airtable_token_here
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX
AIRTABLE_TABLE_NAME=Email Submissions

Deploy the Function

  1. Push to your Git repository
  2. Netlify will automatically deploy
  3. Your function will be available at: https://yoursite.netlify.app/.netlify/functions/verify-email

Step 3: Configure Webflow Form

Add Form to Webflow

  1. In Webflow Designer, add a Form Block
  2. Ensure it has an Email input field with name attribute: email
  3. Publish your site

Set Up Webhook in Webflow

  1. Go to Project Settings → Forms
  2. Find your form in the list
  3. Click “Add Webhook”
  4. Enter your function URL: https://yoursite.netlify.app/.netlify/functions/verify-email
  5. Save settings

Form Submission Flow (Non-Blocking)

When a user submits the form:

  1. User submits → Webflow captures the submission
  2. Webflow shows success → User sees confirmation immediately (no delay)
  3. Webflow triggers webhook → Sends data to your serverless function
  4. Function stores initial record → Creates Airtable entry with “Pending Verification” status
  5. Function returns success → Webhook completes quickly
  6. Background verification starts → checkboxHQ APIs called asynchronously
  7. Airtable record updated → Verification results populate the existing record

Key Benefit: Steps 1-5 happen in under 1 second. Steps 6-7 happen in the background without impacting the user experience or form conversion rates.

Step 4: Enhanced Error Handling (Production-Ready)

// Enhanced version with retry logic for background verification
async function verifyEmailWithRetry(email, maxRetries = 3) {
let lastError;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await verifyEmailWithCheckBox(email);
} catch (error) {
lastError = error;
console.log(`Verification attempt ${attempt} failed for ${email}:`, error.message);
// Wait before retrying (exponential backoff)
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
}
}
}
// After all retries failed, throw error to be caught by verifyAndUpdateAsync
throw new Error(`Failed after ${maxRetries} attempts: ${lastError.message}`);
}
// Graceful degradation - never fail silently
async function verifyAndUpdateAsync(email, submissionId) {
try {
const verificationData = await verifyEmailWithRetry(email);
await updateAirtableRecord(submissionId, verificationData);
console.log(`✓ Verification completed for ${email}`);
} catch (error) {
console.error(`✗ Verification failed for ${email}:`, error);
// Update record with failure status - don't leave in "In Progress" state
await updateAirtableRecord(submissionId, {
verification_error: error.message,
verification_status: 'Failed',
status: 'Verification Error'
}).catch(updateError => {
// Last resort logging if even the update fails
console.error(`Failed to update error status for ${email}:`, updateError);
});
}
}
// Timeout protection for slow API responses
async function withTimeout(promise, timeoutMs = 30000) {
const timeout = new Promise((_, reject) =>
setTimeout(() => reject(new Error('Request timeout')), timeoutMs)
);
return Promise.race([promise, timeout]);
}

Why This Matters for Non-Blocking:

  • User never sees these errors (they already received confirmation)
  • Failed verifications are logged in Airtable for manual review
  • Retries handle temporary API issues without bothering the user
  • Your data remains clean with proper error tracking

Optimization Strategies

Why Non-Blocking Verification Matters

Conversion Rate Impact:

  • Blocking verification: 2-5 second delay = 10-20% form abandonment
  • Non-blocking verification: <1 second response = optimal conversion rates
  • Users get immediate confirmation while quality checks happen behind the scenes

Credit Management

checkboxHQ API endpoints consume different credits:

  • verify_disposable: 1 credit
  • verify_public_provider: 1 credit
  • verify_dns: 2 credits

Cost-Effective Strategy for Background Verification:

async function optimizedBackgroundVerification(email) {
// Strategy 1: Sequential verification (stop early if disposable)
const disposableCheck = await checkDisposable(email);
if (disposableCheck.is_disposable) {
// Stop here - no need to check DNS for disposable emails (saves 2 credits)
return {
...disposableCheck,
verification_type: 'disposable-only',
credits_used: 1
};
}
// Strategy 2: Parallel verification for non-disposable emails
const [dnsCheck, publicCheck] = await Promise.all([
checkDNS(email), // 2 credits
checkPublic(email) // 1 credit
]);
return {
...disposableCheck,
...dnsCheck,
...publicCheck,
verification_type: 'comprehensive',
credits_used: 4
};
}

Batch Processing for High Volume

For sites with high form submission rates:

// Collect submissions in a queue
const verificationQueue = [];
async function queueVerification(email, submissionId) {
verificationQueue.push({ email, submissionId, timestamp: Date.now() });
// Process queue every 10 seconds or when it reaches 10 items
if (verificationQueue.length >= 10) {
await processBatch();
}
}
async function processBatch() {
const batch = verificationQueue.splice(0, 10);
// Process all verifications in parallel
await Promise.all(
batch.map(item => verifyAndUpdateAsync(item.email, item.submissionId))
);
}
// Set up periodic batch processing
setInterval(processBatch, 10000); // Every 10 seconds

Domain-Level Caching (Advanced)

Since verification happens asynchronously, you can implement smart caching to reduce API costs:

// Use external cache (Redis/Upstash) for persistent domain verification
const CACHE_TTL = 7 * 24 * 60 * 60 * 1000; // 7 days
async function getCachedDomainInfo(domain) {
// Check cache first
const cached = await redis.get(`domain:${domain}`);
if (cached) {
const data = JSON.parse(cached);
if (Date.now() - data.timestamp < CACHE_TTL) {
return data.verification;
}
}
return null;
}
async function verifyWithCache(email) {
const domain = email.split('@')[1];
// Check cache for this domain
const cached = await getCachedDomainInfo(domain);
if (cached) {
console.log(`Using cached verification for domain: ${domain}`);
return { ...cached, email, from_cache: true };
}
// Not cached - perform full verification
const verification = await verifyEmailWithCheckBox(email);
// Cache domain-level results
await redis.set(
`domain:${domain}`,
JSON.stringify({
timestamp: Date.now(),
verification: {
is_disposable: verification.is_disposable,
has_mx_record: verification.has_mx_record,
has_a_record: verification.has_a_record,
provider: verification.provider
}
})
);
return verification;
}

Cache Benefits:

  • Second submission from same domain = 0 credits used
  • Instant verification results for popular domains
  • Significant cost savings for high-volume sites
  • Background verification can complete in milliseconds for cached domains

The checkboxHQ Philosophy: Why Non-Blocking?

User Experience First

Blocking VerificationNon-Blocking Verification (checkboxHQ)
User waits 2-5 secondsUser gets instant confirmation (<1s)
Form feels slow/brokenForm feels fast and responsive
10-20% form abandonmentMinimal abandonment
API timeout = failed submissionAPI issues don’t affect users
Poor mobile experienceWorks great on any connection

Business Impact

Higher Conversion Rates:

  • Every second of delay = 7% conversion loss
  • Non-blocking maintains optimal conversion rates
  • Capture the lead, verify in the background

Clean Data Without Trade-offs:

  • Still get full verification results
  • Act on results via automation
  • Better than no verification at all

Operational Reliability:

  • User submissions never fail due to API issues
  • Background retries handle temporary problems
  • Graceful degradation built-in

The Right Way to Use Verification

checkboxHQ advocates for post-submission verification because:

  1. Forms are for capturing leads, not gatekeeping - Your goal is collecting contact information, not preventing submissions
  2. Verification is for data quality, not blocking users - Use results to segment, prioritize, and clean your lists
  3. Users shouldn’t wait for background checks - Email verification is your internal process, not a user-facing obstacle

Smart Actions Based on Verification

Instead of blocking users, use verification data to:

  • Segment your email lists - Different campaigns for business vs. public emails

  • Prioritize sales follow-ups - Focus on verified, non-disposable emails first

  • Flag for manual review - Let humans handle edge cases

  • Track form quality - Monitor which sources attract disposable emails

  • Clean before export - Filter when syncing to CRM/Email Service Provider

  • Trigger different workflows - Send instant confirmation to verified emails, delayed to others

  • Don’t block form submissions - This hurts conversion and user trust

  • Don’t make users wait - Verification delay = lost opportunities

  • Don’t fail forms on API errors - Always capture the lead first

Post-Verification Actions

Airtable Automations for Follow-Up

Once verification completes in the background, you can trigger actions based on results:

Automation 1: Send to CRM (Valid Emails Only)

Trigger: When "Status" = "Valid"
Action: Create record in your CRM or send to Zapier/Make

Automation 2: Flag for Review (Invalid Emails)

Trigger: When "Status" = "Disposable Email" OR "Invalid Domain"
Action: Send Slack notification to team for manual review

Automation 3: Re-engagement Campaign (Public Providers)

Trigger: When "Is Public Provider" = true
Action: Add to different email nurture sequence

Email Marketing Integration

// Add to your verification function
async function sendToEmailMarketing(email, verificationData) {
// Only send verified, non-disposable emails
if (!verificationData.is_disposable && verificationData.has_mx_record) {
await fetch('https://api.youremailprovider.com/subscribe', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.EMAIL_API_KEY}` },
body: JSON.stringify({
email: email,
tags: verificationData.is_public_provider ? ['public-provider'] : ['business-email']
})
});
}
}

Monitoring & Analytics

Track API Usage

Add usage tracking to your function:

async function checkAPIUsage() {
const response = await fetch('https://api.checkboxhq.com/api/v1/billing/usage', {
headers: {
'Authorization': `Bearer ${process.env.CHECKBOX_ACCESS_TOKEN}`
}
});
const usage = await response.json();
console.log(`API Usage: ${usage.requests_used}/${usage.requests_limit} (${usage.percentage_used}%)`);
return usage;
}

Log to Airtable

Create a separate table for logs:

async function logVerification(email, result, status) {
// Store in "Verification Logs" table
await storeInAirtable(
email,
{ ...result, verification_status: status },
'Verification Logs'
);
}

Troubleshooting

Common Issues

Issue: Submissions not appearing in Airtable

  • Check webhook is configured in Webflow settings
  • Verify webhook URL is correct and accessible
  • Check serverless function logs for errors
  • Ensure Airtable credentials are correct in environment variables

Issue: Records stuck in “In Progress” status

  • Background verification may have failed silently
  • Check function logs for errors
  • Verify checkboxHQ API key is valid and has credits
  • Ensure error handling updates records on failure

Issue: Verification taking too long

  • Normal for DNS checks (can take 5-30 seconds)
  • This doesn’t affect user experience (they already got confirmation)
  • Consider implementing caching for popular domains
  • Check if sequential verification strategy could reduce time

Issue: Authentication errors from checkboxHQ

  • Verify x-api-key header is correctly set
  • Check API key is active in checkboxHQ dashboard
  • Ensure environment variables are deployed (not just local)
  • API keys are case-sensitive

Issue: Airtable write failures

  • Verify Personal Access Token has write permissions
  • Check Base ID and Table Name are exactly correct
  • Ensure field names match case-sensitively
  • Check you haven’t exceeded Airtable rate limits (5 requests/second)

Issue: Webhook timeouts

  • This shouldn’t happen with non-blocking pattern
  • Ensure you’re not awaiting verification before returning response
  • Check that storeInitialSubmission completes quickly
  • Verify verifyAndUpdateAsync is truly fire-and-forget

Debug Mode

Add detailed logging for both immediate and background processes:

const DEBUG = process.env.NODE_ENV !== 'production';
function log(stage, ...args) {
if (DEBUG) {
console.log(`[${new Date().toISOString()}] [${stage}]`, ...args);
}
}
// In your handler
exports.handler = async (event, context) => {
const email = payload.data?.email || payload.email;
log('WEBHOOK', 'Received submission for:', email);
await storeInitialSubmission(email, formName, submissionId);
log('STORAGE', 'Initial record created:', submissionId);
verifyAndUpdateAsync(email, submissionId).catch(error => {
log('VERIFY-ERROR', 'Background verification failed:', error);
});
log('RESPONSE', 'Returning success to webhook');
return { statusCode: 200, body: JSON.stringify({ success: true }) };
};
// In background verification
async function verifyAndUpdateAsync(email, submissionId) {
log('VERIFY-START', `Starting verification for ${email}`);
try {
const result = await verifyEmailWithCheckBox(email);
log('VERIFY-SUCCESS', `Verification complete for ${email}`, result);
await updateAirtableRecord(submissionId, result);
log('UPDATE-SUCCESS', `Airtable updated for ${email}`);
} catch (error) {
log('VERIFY-FAIL', `Failed for ${email}:`, error.message);
}
}

Reading Logs:

  1. Check serverless function logs (Netlify/Vercel dashboard)
  2. Look for [WEBHOOK] entries - these should complete in <1 second
  3. Look for [VERIFY-START] entries - these show background processing
  4. Check for [VERIFY-ERROR] entries to debug failed verifications

Security Best Practices

  1. Never expose API keys in client-side code

    • Always use serverless functions as middleware
    • Store credentials in environment variables
  2. Validate webhook sources

    // Add Webflow signature verification
    function verifyWebflowSignature(payload, signature) {
    // Implementation depends on Webflow's webhook signature method
    }
  3. Rate limiting

    const submissions = new Map();
    function checkRateLimit(ip) {
    const now = Date.now();
    const userSubmissions = submissions.get(ip) || [];
    const recentSubmissions = userSubmissions.filter(
    time => now - time < 60000
    );
    if (recentSubmissions.length >= 5) {
    throw new Error('Rate limit exceeded');
    }
    submissions.set(ip, [...recentSubmissions, now]);
    }

Alternative Platforms

Vercel Functions

api/verify-email.js
export default async function handler(req, res) {
if (req.method !== 'POST') {
return res.status(405).json({ error: 'Method not allowed' });
}
// Same logic as Netlify function
// ...
}

Cloudflare Workers

addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
// Same logic adapted for Cloudflare Workers
// ...
}

Cost-Benefit Analysis

Per-Submission Cost Breakdown

Infrastructure Costs (Free Tier Sufficient for Most):

  • Airtable Free: 1,200 records/month
  • Netlify Free: 125,000 function invocations/month
  • Vercel Free: 100,000 function invocations/month

Verification Costs:

Scenario 1: Basic validation (disposable + public provider)

  • checkboxHQ: 2 credits per submission
  • Best for: Basic lead quality scoring
  • Use case: Newsletter signups, content downloads

Scenario 2: Comprehensive validation (all checks)

  • checkboxHQ: 4 credits per submission (disposable + public + DNS)
  • Best for: Sales leads, high-value conversions
  • Use case: Demo requests, contact sales forms

Scenario 3: Smart sequential validation

  • checkboxHQ: 1-4 credits (adaptive based on results)
  • Check disposable first (1 credit)
  • Only check DNS if not disposable (saves 2 credits on 30-40% of submissions)
  • Best for: Cost optimization with high volume

ROI Calculation: Blocking vs Non-Blocking

Example: 1,000 Monthly Form Submissions

MetricBlocking VerificationNon-Blocking (CheckBoxHQ)
Form Load Time3-5 seconds<1 second
Conversion Rate8% (industry avg with delays)10% (no delay penalty)
Actual Conversions80 submissions100 submissions
Lost Opportunities20 submissions0 submissions
Customer ValueMissed $2,000+ potential revenueFull capture

The Math:

  • 2-second delay = 10-20% conversion loss
  • Non-blocking captures 100% of interested visitors
  • Lost conversion costs far exceed verification API costs

Start with comprehensive validation (4 credits):

  1. Captures all leads (no blocking)
  2. Full verification data for segmentation
  3. Optimize later based on your form quality patterns
  4. Worth the investment to maintain high conversion rates

When you reach high volume (>10,000/month):

  • Implement smart sequential validation (saves 30-40% on credits)
  • Add domain caching (saves another 20-30%)
  • Batch processing for efficiency

Next Steps

  1. Test the integration with sample emails
  2. Monitor API usage using the billing endpoint
  3. Set up alerts for low credit balance
  4. Create Airtable views to filter valid/invalid submissions
  5. Build automation with Airtable + Zapier/Make for follow-up

Support

Additional Resources