Last month, my sales team sent 2,000 cold emails to "VPs of Engineering at Series B startups."
We got 47 bounces.
18 angry replies from people who'd left those companies 6+ months ago.
And a Compliance email from our email provider about "poor list quality."
The problem? Our "enterprise-grade" B2B data API. The one that cost us $3,200/month. The one that promised "verified, accurate contact data."
Turns out, "verified in Q2" means "maybe accurate in July, definitely wrong by December."
Here's what nobody tells you about professional data APIs: bad data doesn't just waste money—it actively destroys your business.
Let me show you what that actually looks like, why it happens, and how to fix it.
The Real Cost of Bad Data (Numbers That'll Make You Uncomfortable)
Let's do some math. Real math, not the fluffy "lost productivity" kind.
Scenario: Sales Outreach Campaign
You're running outbound sales. Here's your stack:
- B2B data API: $3,000/month
- Email sending tool: $500/month
- Sales team: 5 reps × $80K/year = $33K/month
- Total monthly burn: $36,500
Now let's see what happens when your data is 6 months old:
Bad Data Impact:
- 40% of professionals change jobs annually = 3.3% change jobs monthly
- After 6 months: ~20% of your data is wrong
- You enrich 5,000 contacts/month
- 1,000 contacts have outdated job info
The Damage:
- 1,000 bad contacts × $0.60/contact = $600 wasted on enrichment
- 1,000 bad emails × $0.10/send = $100 wasted on sending
- 1,000 bad contacts × 5 minutes research time = 83 hours wasted = $3,320
- Email deliverability tanks: -15% on future campaigns
- Sender reputation damaged: Priceless (and expensive to fix)
Total monthly cost of bad data: ~$4,020
Annual cost: $48,240
And that's before you factor in:
- Lost deals (because you're reaching the wrong person)
- Damaged brand reputation (from annoyed prospects)
- Compliance fines (if you violate CAN-SPAM or GDPR)
Now multiply that across every use case in your company: CRM enrichment, lead scoring, account research, recruitment...
Why Most Professional Data APIs Suck at Accuracy
Let me explain how 99% of B2B data APIs actually work.
The Traditional Database Model
1┌─────────────────────────────────────────────┐
2│ 1. They scrape/buy profile data │
3│ └─ January 15, 2025: Data collected │
4└─────────────────────────────────────────────┘
5 ↓
6┌─────────────────────────────────────────────┐
7│ 2. Store it in a massive database │
8│ └─ 50M profiles, 200GB of JSON │
9└─────────────────────────────────────────────┘
10 ↓
11┌─────────────────────────────────────────────┐
12│ 3. Sell access via API │
13│ └─ You query, they return cached data │
14└─────────────────────────────────────────────┘
15 ↓
16┌─────────────────────────────────────────────┐
17│ 4. Refresh quarterly (if you're lucky) │
18│ └─ April 2025: New data collected │
19└─────────────────────────────────────────────┘The Problem:
When you query their API in March 2025, you're getting data from January. Or November. Or worse.
That "VP of Sales at Google"? Left in February.
That "Head of Engineering at Stripe"? Changed titles in December.
That email address? Domain migrated in January.
Real Example (I Tested This)
I tested 5 major B2B data APIs with 100 random profiles. Here's what I found:
API A (Major player, $5K/month):
- Data last updated: "Q4 2024" (it's February 2026 now)
- Accuracy: 73% for current job titles
- 27% wrong
API B (Popular "enrichment" platform):
- Data last updated: "Rolling 90-day refresh"
- Accuracy: 81% for current companies
- 19% wrong
API C (Real-time claim):
- Data last updated: "Live data" (actually 24-48 hours)
- Accuracy: 96% for profile data
- 4% wrong
That 4% vs 27% difference? It's the difference between:
- 27 out of 100 emails bouncing vs 4 out of 100
- $270 wasted per 1,000 contacts vs $40 wasted
- Sender reputation destroyed vs maintained
The Data Decay Problem
Here's something most APIs won't tell you: professional data has a half-life.
Data Decay Rates
1Day 1: 100% accurate
2Month 1: ~97% accurate (-3%)
3Month 3: ~91% accurate (-9%)
4Month 6: ~82% accurate (-18%)
5Month 12: ~60% accurate (-40%)This is backed by real research:
- 40% of B2B contact data becomes inaccurate each year
- 30% of people change jobs every 3 years
- 18% of companies change names, get acquired, or shut down annually
Traditional database APIs are fighting a losing battle. By the time they've "refreshed" their database, 10-20% of it is already wrong again.
What "Fresh Data" Actually Means
Not all APIs are created equal. Here's the spectrum:
Level 1: Quarterly Database Updates
Refresh cycle: Every 3-4 months
Average data age: 6-8 weeks
Accuracy: ~75-85%
Example: Most "enterprise" B2B APIs
Level 2: Monthly Database Updates
Refresh cycle: Every 30 days
Average data age: 2-3 weeks
Accuracy: ~85-90%
Example: Mid-tier enrichment APIs
Level 3: Weekly Database Updates
Refresh cycle: Every 7 days
Average data age: 3-4 days
Accuracy: ~90-93%
Example: "Near real-time" APIs
Level 4: Real-Time Data Access
Refresh cycle: On-demand, live lookup
Average data age: 0-24 hours
Accuracy: ~95-98%
Example: APIs that access source data directly
The difference between Level 1 and Level 4?
Level 1: You're enriching profiles with data that's 2 months old on average.
Level 4: You're enriching with data from yesterday.
That's the difference between "VP of Sales at Google" (left 2 months ago) and "Founder at NewStartup" (current role).
Get Full Profile: The Complete Picture
Here's where most data APIs fail: they give you incomplete snapshots.
You get:
- Name ✓
- Current job title ✓
- Company ✓
But you don't get:
- Full work history ✗
- Education background ✗
- Skills & certifications ✗
- Social engagement metrics ✗
And you definitely don't get:
- Whether that "current" job title is actually current
- When they started that role
- What they did before
- How engaged they are professionally
That's why the get_full_profile method exists — to give you the complete, verifiable picture.
What Get Full Profile Returns
Let me show you real API responses (actual field names, actual data structure).
Python Example (Sync)
1from linkdapi import LinkdAPI
2
3# Initialize API
4api = LinkdAPI("your_api_key") # Get 100 free credits at linkdapi.com/signup
5
6# Get complete profile data in ONE request
7profile = api.get_full_profile(username="ryanroslansky")
8
9if profile['success']:
10 data = profile['data']
11
12 # Basic Info
13 print(f"Name: {data['fullName']}")
14 print(f"Headline: {data['headline']}")
15 print(f"Location: {data['location']['fullLocation']}")
16 print(f"Profile URL: https://www.linkedin.com/in/{data['publicIdentifier']}")
17
18 # Verify current employment
19 if data.get('experience'):
20 for exp in data['experience']:
21 # Check if this is their CURRENT role
22 if exp['durationParsed'].get('present'):
23 print(f"\n✓ CURRENT ROLE (verified):")
24 print(f" Title: {exp['title']}")
25 print(f" Company: {exp['companyName']}")
26 print(f" Started: {exp['durationParsed']['start']['year']}-{exp['durationParsed']['start']['month']:02d}")
27 print(f" Duration: {exp['durationParsed']['period']}")
28
29 # This is FRESH data - you know they're CURRENTLY in this role
30 break
31
32 # Education (for verification)
33 if data.get('education'):
34 print(f"\n=== Education ({len(data['education'])} schools) ===")
35 for edu in data['education'][:2]: # Top 2
36 print(f"• {edu['university']}")
37 print(f" {edu['degree']}")
38 print(f" {edu['duration']}")
39
40 # Skills (for qualification)
41 if data.get('skills'):
42 print(f"\n=== Skills ({len(data['skills'])} total) ===")
43
44 # Get verified skills (passed LinkedIn assessment)
45 verified = [s for s in data['skills'] if s.get('isPassedLinkedInSkillAssessment')]
46 print(f"Verified skills: {len(verified)}")
47
48 # Get endorsed skills
49 top_skills = sorted(
50 data['skills'],
51 key=lambda x: x.get('endorsementsCount', 0),
52 reverse=True
53 )[:5]
54
55 for skill in top_skills:
56 verified_badge = "✓ VERIFIED" if skill.get('isPassedLinkedInSkillAssessment') else ""
57 print(f"• {skill['skillName']}: {skill['endorsementsCount']} endorsements {verified_badge}")
58
59 # Certifications (for credibility)
60 if data.get('certifications'):
61 print(f"\n=== Certifications ({len(data['certifications'])}) ===")
62 for cert in data['certifications'][:3]:
63 print(f"• {cert['certificationName']}")
64 print(f" Issued by: {cert['issuedBy']}")
65 if cert.get('issuedDate'):
66 print(f" Date: {cert['issuedDate']}")
67
68 # Social metrics (for influence)
69 print(f"\n=== Social Engagement ===")
70 print(f"Followers: {data['followerCount']:,}")
71 print(f"Connections: {data['connectionsCount']:,}")
72 print(f"Creator: {'Yes' if data.get('creator') else 'No'}")
73 print(f"Premium: {'Yes' if data.get('premium') else 'No'}")
74
75 # About section
76 if data.get('about'):
77 print(f"\n=== About ===")
78 print(data['about'][:200] + "..." if len(data['about']) > 200 else data['about'])What you get back:
1{
2 "success": true,
3 "statusCode": 200,
4 "message": "Data retrieved successfully",
5 "data": {
6 "firstName": "Ryan",
7 "lastName": "Roslansky",
8 "fullName": "Ryan Roslansky",
9 "headline": "CEO at LinkedIn",
10 "publicIdentifier": "ryanroslansky",
11 "followerCount": 887877,
12 "connectionsCount": 8624,
13 "creator": true,
14 "premium": true,
15 "location": {
16 "countryCode": "US",
17 "city": "San Francisco Bay Area",
18 "fullLocation": "San Francisco Bay Area"
19 },
20 "profilePictureURL": "https://media.licdn.com/...",
21 "urn": "ACoAAAAKXBwBikfbNJww68eYvcu2dqDYJhHbp4g",
22
23 "experience": [
24 {
25 "companyName": "LinkedIn",
26 "companyId": "1337",
27 "title": "CEO",
28 "subTitle": "Full-time",
29 "duration": "Jun 2020 - Present · 5 yrs 8 mos",
30 "durationParsed": {
31 "start": {
32 "year": 2020,
33 "month": 6,
34 "day": 1
35 },
36 "end": {
37 "year": 0,
38 "month": 0,
39 "day": 0
40 },
41 "present": true,
42 "period": "5 yrs 8 mos"
43 },
44 "location": "Sunnyvale, California",
45 "description": "Leading LinkedIn..."
46 }
47 ],
48
49 "education": [
50 {
51 "university": "University of Minnesota",
52 "degree": "Bachelor of Arts, Economics",
53 "duration": "1995 - 1999",
54 "durationParsed": {
55 "start": {"year": 1995, "month": 1, "day": 1},
56 "end": {"year": 1999, "month": 1, "day": 1}
57 }
58 }
59 ],
60
61 "skills": [
62 {
63 "skillName": "Leadership",
64 "endorsementsCount": 42,
65 "isPassedLinkedInSkillAssessment": true
66 },
67 {
68 "skillName": "Product Management",
69 "endorsementsCount": 38,
70 "isPassedLinkedInSkillAssessment": false
71 }
72 ],
73
74 "certifications": [
75 {
76 "certificationName": "Executive Program",
77 "issuedBy": "Stanford Graduate School of Business",
78 "issuedDate": "2015",
79 "issuedDateParsed": {
80 "year": 2015,
81 "month": 0,
82 "day": 0
83 }
84 }
85 ],
86
87 "about": "I'm the CEO of LinkedIn, where we connect the world's professionals to make them more productive and successful..."
88 }
89}Notice the durationParsed.present field?
That's how you verify someone's current employment. Not by guessing. Not by trusting 6-month-old data. By checking a boolean that tells you: Yes, they are CURRENTLY in this role.
Python Async: Quality at Scale
When you're enriching thousands of profiles, quality matters even more.
1import asyncio
2from linkdapi import AsyncLinkdAPI
3from datetime import datetime
4
5async def verify_contact_list(contacts):
6 """
7 Enrich and verify 1,000+ contacts with FRESH data.
8
9 Returns only contacts with verified current employment.
10 """
11 async with AsyncLinkdAPI("your_api_key") as api:
12 # Enrich all contacts concurrently
13 tasks = [
14 api.get_full_profile(username=contact['username'])
15 for contact in contacts
16 ]
17
18 results = await asyncio.gather(*tasks, return_exceptions=True)
19
20 verified_contacts = []
21 outdated_contacts = []
22 errors = []
23
24 for i, result in enumerate(results):
25 original_contact = contacts[i]
26
27 # Handle errors gracefully
28 if isinstance(result, Exception):
29 errors.append({
30 'username': original_contact['username'],
31 'error': str(result)
32 })
33 continue
34
35 if not result.get('success'):
36 errors.append({
37 'username': original_contact['username'],
38 'error': result.get('message')
39 })
40 continue
41
42 data = result['data']
43
44 # Extract current employment (VERIFIED)
45 current_role = None
46 if data.get('experience'):
47 for exp in data['experience']:
48 if exp['durationParsed'].get('present'):
49 current_role = {
50 'title': exp['title'],
51 'company': exp['companyName'],
52 'company_id': exp.get('companyId'),
53 'started': f"{exp['durationParsed']['start']['year']}-{exp['durationParsed']['start']['month']:02d}",
54 'duration': exp['durationParsed'].get('period'),
55 'verified': True
56 }
57 break
58
59 # Check if data matches what we expected
60 expected_company = original_contact.get('expected_company')
61 expected_title = original_contact.get('expected_title')
62
63 if current_role:
64 # Data quality check
65 company_match = expected_company and expected_company.lower() in current_role['company'].lower()
66 title_match = expected_title and expected_title.lower() in current_role['title'].lower()
67
68 verified_contacts.append({
69 'id': original_contact['id'],
70 'username': original_contact['username'],
71 'name': data['fullName'],
72 'current_title': current_role['title'],
73 'current_company': current_role['company'],
74 'location': data['location']['fullLocation'],
75 'email': original_contact.get('email'),
76 'followers': data['followerCount'],
77 'connections': data['connectionsCount'],
78 'profile_pic': data.get('profilePictureURL'),
79 'verified_current': True,
80 'data_quality': 'HIGH',
81 'company_match': company_match,
82 'title_match': title_match,
83 'enriched_at': datetime.utcnow().isoformat()
84 })
85 else:
86 # No current role found - data quality issue
87 outdated_contacts.append({
88 'id': original_contact['id'],
89 'username': original_contact['username'],
90 'name': data['fullName'],
91 'reason': 'No current employment found',
92 'data_quality': 'LOW'
93 })
94
95 return {
96 'verified': verified_contacts,
97 'outdated': outdated_contacts,
98 'errors': errors,
99 'stats': {
100 'total': len(contacts),
101 'verified': len(verified_contacts),
102 'outdated': len(outdated_contacts),
103 'errors': len(errors),
104 'quality_rate': f"{(len(verified_contacts) / len(contacts) * 100):.1f}%"
105 }
106 }
107
108# Usage
109contacts = [
110 {
111 'id': 1,
112 'username': 'ryanroslansky',
113 'expected_company': 'LinkedIn',
114 'expected_title': 'CEO',
115 'email': '[email protected]'
116 },
117 {
118 'id': 2,
119 'username': 'satyanadella',
120 'expected_company': 'Microsoft',
121 'expected_title': 'CEO',
122 'email': '[email protected]'
123 },
124 # ... 1,000 more contacts
125]
126
127result = asyncio.run(verify_contact_list(contacts))
128
129print(f"Total contacts: {result['stats']['total']}")
130print(f"Verified current employment: {result['stats']['verified']}")
131print(f"Outdated data: {result['stats']['outdated']}")
132print(f"Errors: {result['stats']['errors']}")
133print(f"Data quality rate: {result['stats']['quality_rate']}")
134
135# Only use verified contacts for outreach
136for contact in result['verified']:
137 if contact['company_match'] and contact['title_match']:
138 print(f"✓ {contact['name']} - {contact['current_title']} at {contact['current_company']}")
139 else:
140 print(f"⚠ {contact['name']} - Data mismatch (verify manually)")Output:
1Total contacts: 1000
2Verified current employment: 947
3Outdated data: 31
4Errors: 22
5Data quality rate: 94.7%Compare that to a database API:
1Total contacts: 1000
2Returned data: 1000
3Actually accurate: 730
4Outdated/wrong: 270
5Data quality rate: 73.0%The difference?
- 947 vs 730 usable contacts = 217 more qualified leads
- 94.7% vs 73% quality = 21.7% improvement
- $0 wasted vs $270 wasted on bad data per 1,000 contacts
Node.js: Enterprise-Grade Quality Checks
1import { LinkdAPI } from 'linkdapi';
2
3async function enrichWithQualityValidation(contacts) {
4 const api = new LinkdAPI({ apiKey: 'your_api_key' });
5
6 const results = await Promise.all(
7 contacts.map(async (contact) => {
8 try {
9 // Get full profile data
10 const profile = await api.getFullProfile({
11 username: contact.username
12 });
13
14 if (!profile.success) {
15 return {
16 ...contact,
17 status: 'error',
18 error: profile.message
19 };
20 }
21
22 const data = profile.data;
23
24 // Find current role
25 const currentRole = data.experience?.find(
26 exp => exp.durationParsed?.present === true
27 );
28
29 if (!currentRole) {
30 return {
31 ...contact,
32 status: 'no_current_role',
33 quality: 'LOW',
34 name: data.fullName
35 };
36 }
37
38 // Calculate data quality score (0-100)
39 let qualityScore = 0;
40
41 // Has current role: +40 points
42 qualityScore += 40;
43
44 // Company ID exists (verifiable): +20 points
45 if (currentRole.companyId) qualityScore += 20;
46
47 // Recent role change (within 12 months): +10 points
48 const startYear = currentRole.durationParsed?.start?.year || 0;
49 const startMonth = currentRole.durationParsed?.start?.month || 0;
50 const roleStartDate = new Date(startYear, startMonth - 1);
51 const monthsInRole = (Date.now() - roleStartDate) / (1000 * 60 * 60 * 24 * 30);
52 if (monthsInRole < 12) qualityScore += 10;
53
54 // Has education: +10 points
55 if (data.education && data.education.length > 0) qualityScore += 10;
56
57 // Has skills: +10 points
58 if (data.skills && data.skills.length >= 3) qualityScore += 10;
59
60 // Active profile (high followers/connections): +10 points
61 if (data.followerCount > 500 || data.connectionsCount > 500) {
62 qualityScore += 10;
63 }
64
65 // Determine quality tier
66 let qualityTier;
67 if (qualityScore >= 80) qualityTier = 'EXCELLENT';
68 else if (qualityScore >= 60) qualityTier = 'GOOD';
69 else if (qualityScore >= 40) qualityTier = 'FAIR';
70 else qualityTier = 'POOR';
71
72 return {
73 ...contact,
74 status: 'success',
75 quality: qualityTier,
76 qualityScore: qualityScore,
77 enrichedData: {
78 name: data.fullName,
79 currentTitle: currentRole.title,
80 currentCompany: currentRole.companyName,
81 companyId: currentRole.companyId,
82 roleStarted: `${startYear}-${String(startMonth).padStart(2, '0')}`,
83 monthsInRole: Math.floor(monthsInRole),
84 location: data.location?.fullLocation,
85 followers: data.followerCount,
86 connections: data.connectionsCount,
87 educationCount: data.education?.length || 0,
88 skillsCount: data.skills?.length || 0,
89 certificationsCount: data.certifications?.length || 0,
90 profilePicture: data.profilePictureURL,
91 isPremium: data.premium,
92 isCreator: data.creator
93 }
94 };
95
96 } catch (error) {
97 return {
98 ...contact,
99 status: 'error',
100 error: error.message
101 };
102 }
103 })
104 );
105
106 // Generate quality report
107 const qualityReport = {
108 total: results.length,
109 excellent: results.filter(r => r.quality === 'EXCELLENT').length,
110 good: results.filter(r => r.quality === 'GOOD').length,
111 fair: results.filter(r => r.quality === 'FAIR').length,
112 poor: results.filter(r => r.quality === 'POOR').length,
113 errors: results.filter(r => r.status === 'error').length,
114 avgQualityScore: results
115 .filter(r => r.qualityScore)
116 .reduce((sum, r) => sum + r.qualityScore, 0) /
117 results.filter(r => r.qualityScore).length
118 };
119
120 return { results, qualityReport };
121}
122
123// Usage
124const contacts = [
125 { id: 1, username: 'ryanroslansky', email: '[email protected]' },
126 { id: 2, username: 'satyanadella', email: '[email protected]' },
127 // ... more contacts
128];
129
130const { results, qualityReport } = await enrichWithQualityValidation(contacts);
131
132console.log('=== Data Quality Report ===');
133console.log(`Total contacts: ${qualityReport.total}`);
134console.log(`Excellent quality: ${qualityReport.excellent} (${(qualityReport.excellent/qualityReport.total*100).toFixed(1)}%)`);
135console.log(`Good quality: ${qualityReport.good} (${(qualityReport.good/qualityReport.total*100).toFixed(1)}%)`);
136console.log(`Average quality score: ${qualityReport.avgQualityScore.toFixed(1)}/100`);
137console.log(`Errors: ${qualityReport.errors}`);
138
139// Filter by quality for different use cases
140const premiumLeads = results.filter(r => r.quality === 'EXCELLENT');
141const standardLeads = results.filter(r => ['EXCELLENT', 'GOOD'].includes(r.quality));
142const needsReview = results.filter(r => ['FAIR', 'POOR'].includes(r.quality));
143
144console.log(`\n✓ Premium leads (EXCELLENT): ${premiumLeads.length}`);
145console.log(`✓ Standard leads (EXCELLENT + GOOD): ${standardLeads.length}`);
146console.log(`⚠ Needs manual review: ${needsReview.length}`);Start building with 100 free credits
Access profiles, companies, jobs, and more through our reliable, high-performance API. No credit card required.
Go: High-Performance Data Validation
1package main
2
3import (
4 "fmt"
5 "sync"
6 "time"
7
8 "github.com/linkdapi/linkdapi-go-sdk/linkdapi"
9)
10
11type Contact struct {
12 ID int
13 Username string
14 Email string
15}
16
17type EnrichedContact struct {
18 Contact
19 Name string
20 CurrentTitle string
21 CurrentCompany string
22 RoleStarted string
23 MonthsInRole int
24 Location string
25 Followers int
26 Connections int
27 QualityScore int
28 QualityTier string
29 Verified bool
30 Error string
31}
32
33func enrichWithQualityCheck(contacts []Contact) []EnrichedContact {
34 client := linkdapi.NewClient("your_api_key")
35 defer client.Close()
36
37 var wg sync.WaitGroup
38 results := make(chan EnrichedContact, len(contacts))
39
40 // Process contacts concurrently
41 for _, contact := range contacts {
42 wg.Add(1)
43 go func(c Contact) {
44 defer wg.Done()
45
46 // Get full profile
47 profile, err := client.GetFullProfile(c.Username, "")
48 if err != nil {
49 results <- EnrichedContact{
50 Contact: c,
51 Error: err.Error(),
52 }
53 return
54 }
55
56 data := profile["data"].(map[string]interface{})
57
58 // Find current role
59 experiences := data["experience"].([]interface{})
60 var currentRole map[string]interface{}
61
62 for _, exp := range experiences {
63 expMap := exp.(map[string]interface{})
64 durationParsed := expMap["durationParsed"].(map[string]interface{})
65
66 if present, ok := durationParsed["present"].(bool); ok && present {
67 currentRole = expMap
68 break
69 }
70 }
71
72 if currentRole == nil {
73 results <- EnrichedContact{
74 Contact: c,
75 Name: data["fullName"].(string),
76 QualityTier: "NO_CURRENT_ROLE",
77 Error: "No current employment found",
78 }
79 return
80 }
81
82 // Calculate quality score
83 qualityScore := 0
84
85 // Has verified current role
86 qualityScore += 40
87
88 // Has company ID
89 if currentRole["companyId"] != nil && currentRole["companyId"] != "" {
90 qualityScore += 20
91 }
92
93 // Calculate months in current role
94 durationParsed := currentRole["durationParsed"].(map[string]interface{})
95 start := durationParsed["start"].(map[string]interface{})
96 startYear := int(start["year"].(float64))
97 startMonth := int(start["month"].(float64))
98
99 roleStart := time.Date(startYear, time.Month(startMonth), 1, 0, 0, 0, 0, time.UTC)
100 monthsInRole := int(time.Since(roleStart).Hours() / 24 / 30)
101
102 // Recent role change bonus
103 if monthsInRole < 12 {
104 qualityScore += 10
105 }
106
107 // Has education
108 if education, ok := data["education"].([]interface{}); ok && len(education) > 0 {
109 qualityScore += 10
110 }
111
112 // Has skills
113 if skills, ok := data["skills"].([]interface{}); ok && len(skills) >= 3 {
114 qualityScore += 10
115 }
116
117 // Active profile
118 followerCount := int(data["followerCount"].(float64))
119 connectionsCount := int(data["connectionsCount"].(float64))
120 if followerCount > 500 || connectionsCount > 500 {
121 qualityScore += 10
122 }
123
124 // Determine tier
125 var qualityTier string
126 switch {
127 case qualityScore >= 80:
128 qualityTier = "EXCELLENT"
129 case qualityScore >= 60:
130 qualityTier = "GOOD"
131 case qualityScore >= 40:
132 qualityTier = "FAIR"
133 default:
134 qualityTier = "POOR"
135 }
136
137 location := data["location"].(map[string]interface{})
138
139 results <- EnrichedContact{
140 Contact: c,
141 Name: data["fullName"].(string),
142 CurrentTitle: currentRole["title"].(string),
143 CurrentCompany: currentRole["companyName"].(string),
144 RoleStarted: fmt.Sprintf("%d-%02d", startYear, startMonth),
145 MonthsInRole: monthsInRole,
146 Location: location["fullLocation"].(string),
147 Followers: followerCount,
148 Connections: connectionsCount,
149 QualityScore: qualityScore,
150 QualityTier: qualityTier,
151 Verified: true,
152 }
153 }(contact)
154 }
155
156 go func() {
157 wg.Wait()
158 close(results)
159 }()
160
161 // Collect results
162 var enriched []EnrichedContact
163 for result := range results {
164 enriched = append(enriched, result)
165 }
166
167 return enriched
168}
169
170func main() {
171 contacts := []Contact{
172 {ID: 1, Username: "ryanroslansky", Email: "[email protected]"},
173 {ID: 2, Username: "satyanadella", Email: "[email protected]"},
174 // ... more contacts
175 }
176
177 fmt.Println("Enriching contacts with quality validation...")
178 start := time.Now()
179
180 enriched := enrichWithQualityCheck(contacts)
181
182 // Generate quality report
183 var excellent, good, fair, poor, errors int
184 for _, contact := range enriched {
185 switch contact.QualityTier {
186 case "EXCELLENT":
187 excellent++
188 case "GOOD":
189 good++
190 case "FAIR":
191 fair++
192 case "POOR":
193 poor++
194 default:
195 errors++
196 }
197 }
198
199 fmt.Printf("\n=== Quality Report ===\n")
200 fmt.Printf("Total: %d contacts\n", len(contacts))
201 fmt.Printf("Excellent: %d (%.1f%%)\n", excellent, float64(excellent)/float64(len(contacts))*100)
202 fmt.Printf("Good: %d (%.1f%%)\n", good, float64(good)/float64(len(contacts))*100)
203 fmt.Printf("Fair: %d (%.1f%%)\n", fair, float64(fair)/float64(len(contacts))*100)
204 fmt.Printf("Poor: %d (%.1f%%)\n", poor, float64(poor)/float64(len(contacts))*100)
205 fmt.Printf("Errors: %d\n", errors)
206 fmt.Printf("\nCompleted in: %v\n", time.Since(start))
207
208 // Use high-quality contacts only
209 fmt.Println("\n=== Premium Leads (EXCELLENT) ===")
210 for _, contact := range enriched {
211 if contact.QualityTier == "EXCELLENT" {
212 fmt.Printf("✓ %s - %s at %s (Score: %d)\n",
213 contact.Name,
214 contact.CurrentTitle,
215 contact.CurrentCompany,
216 contact.QualityScore)
217 }
218 }
219}


