Data Enrichment Services
Build data enrichment services that augment existing data with external information and intelligence
Data enrichment services augment existing data with additional information from external sources, transforming basic records into comprehensive, actionable datasets. These services are essential for sales, marketing, and business intelligence operations.
Overview
Data enrichment takes incomplete or basic data and enhances it with relevant information from authoritative sources. Whether you're enriching contact records with social profiles, adding company information to leads, or enhancing addresses with geographic coordinates, enrichment services create more valuable and actionable data.
Key Capabilities
- Contact Enrichment: Add email verification, phone numbers, job titles, social profiles
- Company Enrichment: Add firmographic data, revenue, employee count, technology stack
- Geographic Enrichment: Add coordinates, time zones, demographics, boundaries
- Product Enrichment: Add descriptions, images, specifications, reviews
- Lead Scoring: Calculate quality scores based on enriched attributes
- Data Validation: Verify and correct existing data during enrichment
Common Use Cases
- Sales Lead Enrichment: Enhance leads with decision-maker contact info
- Marketing Personalization: Add demographic and behavioral data
- Risk Assessment: Enrich applications with credit and background data
- E-commerce: Enhance product catalogs with detailed information
- Customer Analytics: Add lifetime value predictions and segmentation
- Fraud Detection: Enrich transactions with risk signals
Building Your First Enrichment Service
Let's start with a contact enrichment service:
import $, { ai, db, on, send } from 'sdk.do'
const contactEnrichmentService = await $.Service.create({
name: 'Contact Enricher',
description: 'Enrich contact records with email, phone, social profiles, and company data',
type: $.ServiceType.DataEnrichment,
subtype: 'contact-enrichment',
input: {
required: ['contacts'],
optional: ['enrichmentLevel', 'sources', 'validateExisting'],
},
output: {
enrichedContacts: 'array',
enrichmentRate: 'number',
fieldsAdded: 'number',
validationResults: 'object',
},
enrichmentLevels: {
basic: ['email-verification'],
standard: ['email-verification', 'company-info', 'job-title'],
premium: ['email-verification', 'company-info', 'job-title', 'social-profiles', 'phone-number', 'education'],
},
pricing: {
model: 'per-contact',
levels: {
basic: 0.25,
standard: 0.75,
premium: 1.5,
},
credits: true, // Only charge for successful enrichments
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== contactEnrichmentService.id) return
const { contacts, enrichmentLevel = 'standard', sources, validateExisting } = request.inputs
const enrichmentFields = contactEnrichmentService.enrichmentLevels[enrichmentLevel]
try {
const results = []
let totalFieldsAdded = 0
let successfulEnrichments = 0
for (const contact of contacts) {
const enriched = { ...contact }
const additions = {}
const validations = {}
// Email verification
if (enrichmentFields.includes('email-verification') && contact.email) {
const verification = await verifyEmail(contact.email)
validations.email = verification
if (!verification.valid) {
additions.emailStatus = 'invalid'
additions.emailReason = verification.reason
} else {
additions.emailStatus = 'valid'
additions.emailDeliverability = verification.deliverability
}
totalFieldsAdded += 2
}
// Company information
if (enrichmentFields.includes('company-info') && contact.company) {
const companyData = await enrichCompanyData(contact.company, contact.website)
if (companyData) {
additions.companySize = companyData.employeeCount
additions.companyRevenue = companyData.revenue
additions.companyIndustry = companyData.industry
additions.companyLocation = companyData.headquarters
additions.companyFounded = companyData.foundedYear
totalFieldsAdded += 5
successfulEnrichments++
}
}
// Job title normalization
if (enrichmentFields.includes('job-title') && contact.jobTitle) {
const normalized = await ai.generate({
model: 'gpt-5',
prompt: `Normalize this job title to a standard format: "${contact.jobTitle}"
Provide:
1. Normalized title
2. Seniority level (entry, mid, senior, executive)
3. Department (sales, marketing, engineering, etc.)
4. Decision-making authority (low, medium, high)`,
temperature: 0.1,
})
const parsed = parseJobTitleAnalysis(normalized.content)
additions.jobTitleNormalized = parsed.title
additions.seniorityLevel = parsed.seniority
additions.department = parsed.department
additions.decisionAuthority = parsed.authority
totalFieldsAdded += 4
}
// Social profiles
if (enrichmentFields.includes('social-profiles')) {
const socialProfiles = await findSocialProfiles(contact)
if (socialProfiles.linkedin) {
additions.linkedinUrl = socialProfiles.linkedin
additions.linkedinConnections = socialProfiles.linkedinData?.connections
totalFieldsAdded += 2
}
if (socialProfiles.twitter) {
additions.twitterHandle = socialProfiles.twitter
additions.twitterFollowers = socialProfiles.twitterData?.followers
totalFieldsAdded += 2
}
}
// Phone number
if (enrichmentFields.includes('phone-number') && !contact.phone) {
const phoneData = await findPhoneNumber(contact)
if (phoneData) {
additions.phone = phoneData.number
additions.phoneType = phoneData.type
additions.phoneValid = phoneData.valid
totalFieldsAdded += 3
successfulEnrichments++
}
}
// Education
if (enrichmentFields.includes('education')) {
const education = await findEducation(contact)
if (education) {
additions.education = education.schools
additions.highestDegree = education.highestDegree
totalFieldsAdded += 2
}
}
// Merge enriched data
Object.assign(enriched, additions)
results.push({
original: contact,
enriched,
fieldsAdded: Object.keys(additions).length,
validations,
})
if (Object.keys(additions).length > 0) {
successfulEnrichments++
}
}
// Calculate enrichment rate
const enrichmentRate = (successfulEnrichments / contacts.length) * 100
// Deliver results
await send.ServiceResult.deliver({
requestId: request.id,
outputs: {
enrichedContacts: results.map((r) => r.enriched),
enrichmentRate,
fieldsAdded: totalFieldsAdded,
validationResults: results.map((r) => r.validations),
summary: {
total: contacts.length,
enriched: successfulEnrichments,
failed: contacts.length - successfulEnrichments,
},
},
})
// Charge only for successful enrichments
const pricePerContact = contactEnrichmentService.pricing.levels[enrichmentLevel]
const cost = successfulEnrichments * pricePerContact
await send.Payment.charge({
customerId: request.customerId,
amount: cost,
description: `Contact enrichment (${successfulEnrichments} contacts, ${enrichmentLevel} level)`,
})
} catch (error) {
await send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: true,
})
}
})
async function verifyEmail(email: string): Promise<any> {
// Email verification logic
const syntax = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
if (!syntax) {
return { valid: false, reason: 'invalid-syntax' }
}
// Check MX records, SMTP, etc.
const mx = await checkMXRecords(email)
const smtp = await checkSMTP(email)
return {
valid: mx && smtp,
deliverability: smtp ? 'high' : 'low',
reason: !mx ? 'no-mx-records' : !smtp ? 'mailbox-not-found' : null,
}
}
async function enrichCompanyData(companyName: string, website?: string): Promise<any> {
// Lookup company data from multiple sources
const sources = [lookupClearbit(companyName, website), lookupCrunchbase(companyName), lookupLinkedIn(companyName)]
const results = await Promise.allSettled(sources)
const successful = results.filter((r) => r.status === 'fulfilled').map((r) => r.value)
if (successful.length === 0) return null
// Merge data from multiple sources
return mergeCompanyData(successful)
}Lead Scoring Service
Calculate quality scores based on enriched data:
const leadScoringService = await $.Service.create({
name: 'AI Lead Scorer',
description: 'Score and prioritize leads based on enriched data and behavior',
type: $.ServiceType.DataEnrichment,
subtype: 'lead-scoring',
input: {
required: ['leads'],
optional: ['scoringModel', 'weights', 'threshold'],
},
features: ['demographic-scoring', 'firmographic-scoring', 'behavioral-scoring', 'predictive-scoring', 'fit-score', 'engagement-score'],
pricing: {
model: 'per-lead',
rate: 0.1,
minimumCharge: 5.0,
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== leadScoringService.id) return
const { leads, scoringModel = 'standard', weights, threshold = 50 } = request.inputs
try {
const scoredLeads = []
for (const lead of leads) {
// First, enrich the lead with additional data
const enriched = await enrichLeadData(lead)
// Calculate demographic score (0-100)
const demographicScore = calculateDemographicScore(enriched, weights?.demographic)
// Calculate firmographic score (0-100)
const firmographicScore = calculateFirmographicScore(enriched, weights?.firmographic)
// Calculate behavioral score (0-100)
const behavioralScore = calculateBehavioralScore(enriched, weights?.behavioral)
// Use AI for predictive scoring
const predictiveScore = await ai.predict({
model: 'gpt-5',
type: 'lead-conversion-probability',
features: {
...enriched,
demographicScore,
firmographicScore,
behavioralScore,
},
historicalData: await getHistoricalConversions(),
})
// Calculate composite score
const compositeScore = calculateCompositeScore(
{
demographic: demographicScore,
firmographic: firmographicScore,
behavioral: behavioralScore,
predictive: predictiveScore.probability * 100,
},
weights
)
// Classify lead
const classification = classifyLead(compositeScore, threshold)
scoredLeads.push({
...enriched,
scores: {
composite: compositeScore,
demographic: demographicScore,
firmographic: firmographicScore,
behavioral: behavioralScore,
predictive: predictiveScore.probability * 100,
},
classification,
conversionProbability: predictiveScore.probability,
recommendedActions: predictiveScore.recommendations,
})
}
// Sort by score
scoredLeads.sort((a, b) => b.scores.composite - a.scores.composite)
// Deliver results
await send.ServiceResult.deliver({
requestId: request.id,
outputs: {
leads: scoredLeads,
summary: {
total: leads.length,
hot: scoredLeads.filter((l) => l.classification === 'hot').length,
warm: scoredLeads.filter((l) => l.classification === 'warm').length,
cold: scoredLeads.filter((l) => l.classification === 'cold').length,
averageScore: scoredLeads.reduce((sum, l) => sum + l.scores.composite, 0) / scoredLeads.length,
},
},
})
// Charge for scoring
const cost = Math.max(leads.length * leadScoringService.pricing.rate, leadScoringService.pricing.minimumCharge)
await send.Payment.charge({
customerId: request.customerId,
amount: cost,
description: `Lead scoring (${leads.length} leads)`,
})
} catch (error) {
await send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: true,
})
}
})
function calculateDemographicScore(lead: any, weights?: any): number {
let score = 0
const w = weights || { jobTitle: 30, seniority: 25, department: 20, email: 15, phone: 10 }
// Job title scoring
if (lead.jobTitleNormalized) {
const titleScore = scoreJobTitle(lead.jobTitleNormalized)
score += (titleScore / 100) * w.jobTitle
}
// Seniority scoring
if (lead.seniorityLevel) {
const seniorityMap = { executive: 100, senior: 75, mid: 50, entry: 25 }
score += (seniorityMap[lead.seniorityLevel] || 0) * (w.seniority / 100)
}
// Department scoring
if (lead.department) {
const departmentMap = { executive: 100, sales: 80, marketing: 70, it: 60 }
score += (departmentMap[lead.department] || 50) * (w.department / 100)
}
// Contact info completeness
if (lead.emailStatus === 'valid') score += w.email
if (lead.phoneValid) score += w.phone
return Math.min(score, 100)
}
function calculateFirmographicScore(lead: any, weights?: any): number {
let score = 0
const w = weights || { companySize: 30, revenue: 25, industry: 20, location: 15, technology: 10 }
// Company size scoring
if (lead.companySize) {
const sizeScore = scoreCompanySize(lead.companySize)
score += (sizeScore / 100) * w.companySize
}
// Revenue scoring
if (lead.companyRevenue) {
const revenueScore = scoreRevenue(lead.companyRevenue)
score += (revenueScore / 100) * w.revenue
}
// Industry match scoring
if (lead.companyIndustry) {
const industryScore = scoreIndustry(lead.companyIndustry)
score += (industryScore / 100) * w.industry
}
// Location scoring
if (lead.companyLocation) {
const locationScore = scoreLocation(lead.companyLocation)
score += (locationScore / 100) * w.location
}
return Math.min(score, 100)
}Geographic Enrichment Service
Add location-based data to records:
const geoEnrichmentService = await $.Service.create({
name: 'Geographic Enricher',
description: 'Enrich addresses with coordinates, time zones, and demographic data',
type: $.ServiceType.DataEnrichment,
subtype: 'geo-enrichment',
input: {
required: ['records'],
optional: ['addressField', 'enrichmentFields'],
},
enrichmentFields: ['coordinates', 'timezone', 'country-code', 'postal-code', 'census-data', 'demographics', 'weather', 'nearby-places'],
pricing: {
model: 'per-geocode',
rate: 0.01,
additionalFields: 0.005, // Per additional field
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== geoEnrichmentService.id) return
const { records, addressField = 'address', enrichmentFields = ['coordinates', 'timezone'] } = request.inputs
try {
const enrichedRecords = []
let totalCost = 0
for (const record of records) {
const address = record[addressField]
if (!address) {
enrichedRecords.push(record)
continue
}
const enriched = { ...record }
// Geocode address
const geocoded = await geocodeAddress(address)
if (!geocoded) {
enrichedRecords.push(record)
continue
}
// Add base geocoding (always included)
enriched.latitude = geocoded.lat
enriched.longitude = geocoded.lng
enriched.formattedAddress = geocoded.formattedAddress
totalCost += geoEnrichmentService.pricing.rate
// Add optional fields
for (const field of enrichmentFields) {
switch (field) {
case 'timezone':
enriched.timezone = await getTimezone(geocoded.lat, geocoded.lng)
totalCost += geoEnrichmentService.pricing.additionalFields
break
case 'country-code':
enriched.countryCode = geocoded.countryCode
enriched.countryName = geocoded.countryName
totalCost += geoEnrichmentService.pricing.additionalFields
break
case 'postal-code':
enriched.postalCode = geocoded.postalCode
totalCost += geoEnrichmentService.pricing.additionalFields
break
case 'census-data':
enriched.censusData = await getCensusData(geocoded.lat, geocoded.lng)
totalCost += geoEnrichmentService.pricing.additionalFields
break
case 'demographics':
enriched.demographics = await getDemographics(geocoded.lat, geocoded.lng)
totalCost += geoEnrichmentService.pricing.additionalFields
break
case 'weather':
enriched.weather = await getWeather(geocoded.lat, geocoded.lng)
totalCost += geoEnrichmentService.pricing.additionalFields
break
case 'nearby-places':
enriched.nearbyPlaces = await getNearbyPlaces(geocoded.lat, geocoded.lng)
totalCost += geoEnrichmentService.pricing.additionalFields
break
}
}
enrichedRecords.push(enriched)
}
// Deliver results
await send.ServiceResult.deliver({
requestId: request.id,
outputs: {
records: enrichedRecords,
geocodedCount: enrichedRecords.filter((r) => r.latitude).length,
},
})
// Charge for enrichment
await send.Payment.charge({
customerId: request.customerId,
amount: totalCost,
description: `Geographic enrichment (${enrichedRecords.length} records)`,
})
} catch (error) {
await send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: true,
})
}
})
async function geocodeAddress(address: string): Promise<any> {
// Call geocoding API (Google Maps, Mapbox, etc.)
try {
const response = await fetch(
`https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${process.env.GOOGLE_MAPS_API_KEY}`
)
const data = await response.json()
if (data.results && data.results.length > 0) {
const result = data.results[0]
return {
lat: result.geometry.location.lat,
lng: result.geometry.location.lng,
formattedAddress: result.formatted_address,
countryCode: extractCountryCode(result),
countryName: extractCountryName(result),
postalCode: extractPostalCode(result),
}
}
return null
} catch (error) {
return null
}
}Product Enrichment Service
Enhance product catalogs with detailed information:
const productEnrichmentService = await $.Service.create({
name: 'Product Data Enricher',
description: 'Enrich product catalogs with descriptions, images, specs, and reviews',
type: $.ServiceType.DataEnrichment,
subtype: 'product-enrichment',
input: {
required: ['products'],
optional: ['enrichmentFields', 'sources', 'language'],
},
enrichmentFields: ['description', 'images', 'specifications', 'reviews', 'pricing', 'availability', 'alternatives'],
pricing: {
model: 'per-product',
base: 0.5,
fields: {
description: 0.1,
images: 0.15,
specifications: 0.2,
reviews: 0.1,
pricing: 0.05,
},
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== productEnrichmentService.id) return
const { products, enrichmentFields, language = 'en' } = request.inputs
try {
const enrichedProducts = []
for (const product of products) {
const enriched = { ...product }
let productCost = productEnrichmentService.pricing.base
// Generate/enhance description
if (enrichmentFields.includes('description') && !product.description) {
enriched.description = await ai.generate({
model: 'gpt-5',
prompt: `Write a compelling product description for:
Product: ${product.name}
Category: ${product.category}
Features: ${product.features?.join(', ')}
The description should be 2-3 paragraphs, highlighting benefits and use cases.`,
temperature: 0.7,
})
productCost += productEnrichmentService.pricing.fields.description
}
// Find or generate images
if (enrichmentFields.includes('images')) {
const existingImages = product.images || []
if (existingImages.length < 3) {
const additionalImages = await findProductImages(product)
enriched.images = [...existingImages, ...additionalImages]
productCost += productEnrichmentService.pricing.fields.images
}
}
// Gather specifications
if (enrichmentFields.includes('specifications')) {
const specs = await gatherProductSpecifications(product)
enriched.specifications = {
...product.specifications,
...specs,
}
productCost += productEnrichmentService.pricing.fields.specifications
}
// Aggregate reviews
if (enrichmentFields.includes('reviews')) {
const reviews = await aggregateProductReviews(product)
enriched.reviews = reviews
enriched.averageRating = calculateAverageRating(reviews)
enriched.reviewCount = reviews.length
productCost += productEnrichmentService.pricing.fields.reviews
}
// Check competitive pricing
if (enrichmentFields.includes('pricing')) {
const priceComparison = await compareProductPricing(product)
enriched.priceComparison = priceComparison
enriched.priceCompetitiveness = analyzePriceCompetitiveness(product.price, priceComparison)
productCost += productEnrichmentService.pricing.fields.pricing
}
// Find alternatives
if (enrichmentFields.includes('alternatives')) {
enriched.alternatives = await findAlternativeProducts(product)
}
enrichedProducts.push(enriched)
}
// Deliver results
await send.ServiceResult.deliver({
requestId: request.id,
outputs: {
products: enrichedProducts,
summary: {
total: products.length,
fieldsEnriched: enrichmentFields,
},
},
})
// Calculate total cost
const totalCost = products.length * productCost
await send.Payment.charge({
customerId: request.customerId,
amount: totalCost,
description: `Product enrichment (${products.length} products)`,
})
} catch (error) {
await send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: true,
})
}
})Real-Time Enrichment Service
Enrich data in real-time as it flows through your system:
const realtimeEnrichmentService = await $.Service.create({
name: 'Real-Time Data Enricher',
description: 'Enrich data streams in real-time with sub-100ms latency',
type: $.ServiceType.DataEnrichment,
subtype: 'real-time',
latency: '<100ms',
throughput: '10000 records/second',
pricing: {
model: 'subscription',
tiers: [
{ name: 'starter', price: 200, throughput: '1000 records/sec', enrichments: ['basic'] },
{ name: 'professional', price: 1000, throughput: '5000 records/sec', enrichments: ['basic', 'standard'] },
{ name: 'enterprise', price: 5000, throughput: '25000 records/sec', enrichments: ['basic', 'standard', 'premium'] },
],
},
})
on.Stream.data(async (stream) => {
const enrichmentConfig = await db.EnrichmentConfig.get({
where: { streamId: stream.id },
})
if (!enrichmentConfig) return
// Create enrichment cache for performance
const cache = new Map()
for await (const record of stream) {
try {
const enriched = { ...record }
// Apply enrichments based on config
for (const enrichment of enrichmentConfig.enrichments) {
const cacheKey = `${enrichment.type}:${record[enrichment.keyField]}`
// Check cache first
if (cache.has(cacheKey)) {
Object.assign(enriched, cache.get(cacheKey))
continue
}
// Perform enrichment
const enrichmentData = await performEnrichment(record, enrichment)
if (enrichmentData) {
Object.assign(enriched, enrichmentData)
// Cache for 5 minutes
cache.set(cacheKey, enrichmentData)
setTimeout(() => cache.delete(cacheKey), 5 * 60 * 1000)
}
}
// Emit enriched record
await send.Stream.emit({
streamId: stream.id,
data: enriched,
latency: Date.now() - record.timestamp,
})
} catch (error) {
// Emit original record on error
await send.Stream.emit({
streamId: stream.id,
data: record,
enrichmentError: error.message,
})
}
}
})
async function performEnrichment(record: any, config: any): Promise<any> {
switch (config.type) {
case 'ip-geolocation':
return await enrichIPGeolocation(record[config.keyField])
case 'user-agent-parsing':
return await parseUserAgent(record[config.keyField])
case 'company-lookup':
return await lookupCompany(record[config.keyField])
case 'email-domain':
return await enrichEmailDomain(record[config.keyField])
case 'phone-carrier':
return await lookupPhoneCarrier(record[config.keyField])
default:
return null
}
}Batch Enrichment Service
Enrich large datasets efficiently:
const batchEnrichmentService = await $.Service.create({
name: 'Batch Data Enricher',
description: 'Efficiently enrich large datasets with optimized batch processing',
type: $.ServiceType.DataEnrichment,
subtype: 'batch',
maxBatchSize: 100000,
averageProcessingTime: '1000 records/minute',
pricing: {
model: 'per-thousand',
rate: 10.0, // $10 per 1000 records
volume: [
{ min: 0, max: 10000, rate: 10.0 },
{ min: 10001, max: 100000, rate: 7.5 },
{ min: 100001, max: Infinity, rate: 5.0 },
],
},
})
on.ServiceRequest.created(async (request) => {
if (request.serviceId !== batchEnrichmentService.id) return
const { dataUrl, enrichmentConfig, outputFormat = 'json' } = request.inputs
try {
// Download dataset
await send.ServiceProgress.updated({
requestId: request.id,
stage: 'downloading',
progress: 0,
})
const dataset = await downloadDataset(dataUrl)
// Process in batches
const batchSize = 1000
const enrichedData = []
for (let i = 0; i < dataset.length; i += batchSize) {
const batch = dataset.slice(i, i + batchSize)
// Enrich batch in parallel
const enrichedBatch = await Promise.all(
batch.map(async (record) => {
const enriched = { ...record }
for (const enrichment of enrichmentConfig) {
const data = await getEnrichmentData(record, enrichment)
if (data) Object.assign(enriched, data)
}
return enriched
})
)
enrichedData.push(...enrichedBatch)
// Update progress
await send.ServiceProgress.updated({
requestId: request.id,
stage: 'enriching',
progress: (i + batchSize) / dataset.length,
message: `Enriched ${Math.min(i + batchSize, dataset.length)} of ${dataset.length} records`,
})
}
// Upload result
const resultUrl = await uploadEnrichedDataset(enrichedData, outputFormat)
// Deliver results
await send.ServiceResult.deliver({
requestId: request.id,
outputs: {
url: resultUrl,
recordCount: enrichedData.length,
enrichmentRate: calculateEnrichmentRate(dataset, enrichedData),
},
})
// Calculate cost
const thousands = Math.ceil(dataset.length / 1000)
const rate = getVolumeRate(dataset.length, batchEnrichmentService.pricing.volume)
const cost = thousands * rate
await send.Payment.charge({
customerId: request.customerId,
amount: cost,
description: `Batch enrichment (${dataset.length} records)`,
})
} catch (error) {
await send.ServiceRequest.fail({
requestId: request.id,
error: error.message,
retryable: true,
})
}
})Pricing Models for Enrichment Services
Per-Record Pricing
Best for: Contact and lead enrichment
pricing: {
model: 'per-record',
rate: 0.50,
creditsOnly: true, // Only charge for successful enrichments
}Tiered Pricing
Best for: Different enrichment levels
pricing: {
model: 'tiered',
levels: {
basic: 0.25,
standard: 0.75,
premium: 1.50,
},
}Subscription Pricing
Best for: Real-time and high-volume enrichment
pricing: {
model: 'subscription',
tiers: [
{ name: 'starter', price: 200, records: 10000 },
{ name: 'professional', price: 1000, records: 100000 },
{ name: 'enterprise', price: 5000, records: 1000000 },
],
}Credit-Based Pricing
Best for: Variable enrichment needs
pricing: {
model: 'credits',
creditCost: 1.0, // $1 = 100 credits
enrichmentCosts: {
emailVerification: 1,
phoneNumber: 5,
socialProfiles: 10,
companyData: 15,
},
}Best Practices
1. Cache Enrichment Results
Avoid redundant API calls:
const enrichmentCache = new Map()
async function getCachedEnrichment(key: string, enrichFn: () => Promise<any>, ttl = 3600000): Promise<any> {
if (enrichmentCache.has(key)) {
const cached = enrichmentCache.get(key)
if (Date.now() - cached.timestamp < ttl) {
return cached.data
}
}
const data = await enrichFn()
enrichmentCache.set(key, { data, timestamp: Date.now() })
return data
}2. Handle API Rate Limits
Implement retry logic:
async function enrichWithRetry(fn: () => Promise<any>, maxRetries = 3): Promise<any> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn()
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
// Rate limited, wait and retry
await sleep(Math.pow(2, i) * 1000)
continue
}
throw error
}
}
}3. Validate Enriched Data
Ensure quality:
function validateEnrichment(original: any, enriched: any, config: any): boolean {
// Check that enrichment added expected fields
for (const field of config.requiredFields) {
if (!enriched[field]) return false
}
// Check data quality
for (const [field, validator] of Object.entries(config.validators)) {
if (enriched[field] && !validator(enriched[field])) {
return false
}
}
return true
}Next Steps
- Data Transformation Services → - Convert and normalize data
- Data Validation Services → - Ensure data quality
- Data Analytics Services → - Generate insights
- Service Composition → - Combine enrichment services