Building Manglik Dosha Detection for Matrimonial Apps
Complete guide to implementing Manglik, Kalsarpa, and Sadhesati dosha checkers using Vedic Astrology API. Perfect for marriage matchmaking platforms.
Building Manglik Dosha Detection for Matrimonial Apps
Dosha detection is critical for matrimonial platforms in India. Manglik Dosha (Mars affliction) alone affects match filtering for millions of users. This tutorial shows you how to build a complete dosha checking system using real Vedic Astrology APIs, including interpretation logic and user-friendly UI components.
What We Will Build
Our dosha detection system includes:
- Manglik Dosha Checker - Mars in houses 1, 2, 4, 7, 8, 12
- Kalsarpa Dosha Checker - All planets between Rahu-Ketu axis
- Sadhesati Checker - Saturn transit through 12th, 1st, 2nd from Moon
- Combined Dosha Report - All three doshas in single view
- Match Filter Integration - Filter profiles by dosha compatibility
- Remedies Suggestions - Traditional remedies for each dosha
- Dosha Severity Levels - Partial vs Full Manglik classification
Understanding the APIs
Let's first verify the actual API response structures:
Manglik Dosha API Response
{
"present": false,
"description": "No Manglik Dosha detected. Mars is not in afflicting houses (1, 2, 4, 7, 8, 12)."
}
Kalsarpa Dosha API Response
{
"present": false,
"description": "No Kalsarpa Dosha. Not all planets are between Rahu-Ketu axis."
}
Sadhesati API Response
{
"present": false,
"description": "Not in Sadhesati period. Saturn is not in 12th, 1st, or 2nd house from natal Moon."
}
All three endpoints return simple boolean + description format.
Step 1: API Client Setup
Create a dosha checking service:
// services/dosha-checker.ts
interface BirthData {
date: string;
time: string;
latitude: number;
longitude: number;
timezone: number;
}
interface DoshaResult {
present: boolean;
description: string;
}
interface CompleteDoshaReport {
manglik: DoshaResult;
kalsarpa: DoshaResult;
sadhesati: DoshaResult;
birthData: BirthData;
timestamp: string;
}
export class DoshaCheckerService {
private apiKey: string;
private baseURL: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
this.baseURL = 'https://roxyapi.com/api/v2/vedic-astrology';
}
async checkManglikDosha(birthData: BirthData): Promise<DoshaResult> {
const response = await fetch(`${this.baseURL}/dosha/manglik`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
},
body: JSON.stringify(birthData),
});
if (!response.ok) {
throw new Error(`Manglik check failed: ${response.statusText}`);
}
return await response.json();
}
async checkKalsarpaDosha(birthData: BirthData): Promise<DoshaResult> {
const response = await fetch(`${this.baseURL}/dosha/kalsarpa`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
},
body: JSON.stringify(birthData),
});
if (!response.ok) {
throw new Error(`Kalsarpa check failed: ${response.statusText}`);
}
return await response.json();
}
async checkSadhesati(birthData: BirthData): Promise<DoshaResult> {
const response = await fetch(`${this.baseURL}/dosha/sadhesati`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
},
body: JSON.stringify(birthData),
});
if (!response.ok) {
throw new Error(`Sadhesati check failed: ${response.statusText}`);
}
return await response.json();
}
async checkAllDoshas(birthData: BirthData): Promise<CompleteDoshaReport> {
// Run all checks in parallel
const [manglik, kalsarpa, sadhesati] = await Promise.all([
this.checkManglikDosha(birthData),
this.checkKalsarpaDosha(birthData),
this.checkSadhesati(birthData),
]);
return {
manglik,
kalsarpa,
sadhesati,
birthData,
timestamp: new Date().toISOString(),
};
}
}
Step 2: Manglik Dosha UI Component
Build a user-friendly display:
// components/ManglikChecker.tsx
import React, { useState } from 'react';
import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native';
import { DoshaCheckerService } from '../services/dosha-checker';
interface Props {
birthData: BirthData;
apiKey: string;
}
export function ManglikChecker({ birthData, apiKey }: Props) {
const [result, setResult] = useState<DoshaResult | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function checkDosha() {
setLoading(true);
setError(null);
try {
const service = new DoshaCheckerService(apiKey);
const manglikResult = await service.checkManglikDosha(birthData);
setResult(manglikResult);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to check dosha');
} finally {
setLoading(false);
}
}
return (
<View style={styles.container}>
<View style={styles.header}>
<Text style={styles.title}>Manglik Dosha Check</Text>
<Text style={styles.subtitle}>
Mars in houses 1, 2, 4, 7, 8, or 12 causes Mangal Dosha
</Text>
</View>
{!result && !loading && (
<TouchableOpacity style={styles.checkButton} onPress={checkDosha}>
<Text style={styles.checkButtonText}>Check Manglik Status</Text>
</TouchableOpacity>
)}
{loading && (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#DC2626" />
<Text style={styles.loadingText}>Analyzing birth chart...</Text>
</View>
)}
{error && (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>⚠️ {error}</Text>
<TouchableOpacity style={styles.retryButton} onPress={checkDosha}>
<Text style={styles.retryButtonText}>Retry</Text>
</TouchableOpacity>
</View>
)}
{result && (
<View style={[
styles.resultContainer,
result.present ? styles.resultPresent : styles.resultAbsent
]}>
<View style={styles.resultHeader}>
<Text style={styles.resultIcon}>
{result.present ? '⚠️' : '✅'}
</Text>
<Text style={styles.resultTitle}>
{result.present ? 'Manglik Dosha Present' : 'No Manglik Dosha'}
</Text>
</View>
<Text style={styles.resultDescription}>
{result.description}
</Text>
{result.present && (
<View style={styles.remediesSection}>
<Text style={styles.remediesTitle}>Traditional Remedies:</Text>
<Text style={styles.remedyItem}>
• Kumbh Vivah (marriage to tree/idol before actual marriage)
</Text>
<Text style={styles.remedyItem}>
• Recite Hanuman Chalisa daily
</Text>
<Text style={styles.remedyItem}>
• Fast on Tuesdays
</Text>
<Text style={styles.remedyItem}>
• Donate red lentils on Tuesdays
</Text>
<Text style={styles.remedyItem}>
• Worship Lord Hanuman
</Text>
</View>
)}
<TouchableOpacity
style={styles.recheckButton}
onPress={() => setResult(null)}
>
<Text style={styles.recheckButtonText}>Check Again</Text>
</TouchableOpacity>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
padding: 20,
marginVertical: 10,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 3,
},
header: {
marginBottom: 20,
},
title: {
fontSize: 20,
fontWeight: 'bold',
color: '#DC2626',
marginBottom: 8,
},
subtitle: {
fontSize: 14,
color: '#6B7280',
lineHeight: 20,
},
checkButton: {
backgroundColor: '#DC2626',
paddingVertical: 16,
borderRadius: 12,
alignItems: 'center',
},
checkButtonText: {
color: '#FFFFFF',
fontSize: 16,
fontWeight: '600',
},
loadingContainer: {
alignItems: 'center',
paddingVertical: 40,
},
loadingText: {
marginTop: 16,
fontSize: 14,
color: '#6B7280',
},
errorContainer: {
backgroundColor: '#FEE2E2',
padding: 16,
borderRadius: 12,
alignItems: 'center',
},
errorText: {
fontSize: 14,
color: '#991B1B',
marginBottom: 12,
textAlign: 'center',
},
retryButton: {
backgroundColor: '#DC2626',
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
},
retryButtonText: {
color: '#FFFFFF',
fontSize: 14,
fontWeight: '600',
},
resultContainer: {
padding: 20,
borderRadius: 12,
borderWidth: 2,
},
resultPresent: {
backgroundColor: '#FEF3C7',
borderColor: '#F59E0B',
},
resultAbsent: {
backgroundColor: '#D1FAE5',
borderColor: '#10B981',
},
resultHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
},
resultIcon: {
fontSize: 32,
marginRight: 12,
},
resultTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1F2937',
flex: 1,
},
resultDescription: {
fontSize: 14,
color: '#4B5563',
lineHeight: 20,
marginBottom: 16,
},
remediesSection: {
backgroundColor: '#FFFFFF',
padding: 16,
borderRadius: 8,
marginBottom: 16,
},
remediesTitle: {
fontSize: 16,
fontWeight: '600',
color: '#1F2937',
marginBottom: 12,
},
remedyItem: {
fontSize: 14,
color: '#4B5563',
lineHeight: 22,
marginBottom: 4,
},
recheckButton: {
backgroundColor: '#FFFFFF',
paddingVertical: 12,
borderRadius: 8,
alignItems: 'center',
borderWidth: 1,
borderColor: '#D1D5DB',
},
recheckButtonText: {
color: '#4B5563',
fontSize: 14,
fontWeight: '600',
},
});
Step 3: Complete Dosha Report Component
Show all three doshas together:
// components/CompleteDoshaReport.tsx
import React, { useState, useEffect } from 'react';
import { View, Text, StyleSheet, ScrollView, ActivityIndicator } from 'react-native';
import { DoshaCheckerService, CompleteDoshaReport } from '../services/dosha-checker';
interface Props {
birthData: BirthData;
apiKey: string;
}
export function CompleteDoshaReport({ birthData, apiKey }: Props) {
const [report, setReport] = useState<CompleteDoshaReport | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
loadReport();
}, [birthData]);
async function loadReport() {
setLoading(true);
try {
const service = new DoshaCheckerService(apiKey);
const fullReport = await service.checkAllDoshas(birthData);
setReport(fullReport);
} catch (error) {
console.error('Failed to load dosha report:', error);
} finally {
setLoading(false);
}
}
if (loading) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#6366F1" />
<Text style={styles.loadingText}>Analyzing doshas...</Text>
</View>
);
}
if (!report) {
return (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Failed to load dosha report</Text>
</View>
);
}
const doshaCount = [
report.manglik.present,
report.kalsarpa.present,
report.sadhesati.present,
].filter(Boolean).length;
return (
<ScrollView style={styles.container}>
{/* Summary Card */}
<View style={styles.summaryCard}>
<Text style={styles.summaryTitle}>Dosha Analysis Summary</Text>
<View style={styles.summaryContent}>
<View style={styles.summaryItem}>
<Text style={styles.summaryLabel}>Doshas Found:</Text>
<Text style={[
styles.summaryValue,
doshaCount > 0 ? styles.summaryWarning : styles.summaryGood
]}>
{doshaCount} / 3
</Text>
</View>
<View style={styles.summaryItem}>
<Text style={styles.summaryLabel}>Overall Status:</Text>
<Text style={[
styles.summaryValue,
doshaCount > 0 ? styles.summaryWarning : styles.summaryGood
]}>
{doshaCount === 0 ? 'Clear' : doshaCount === 1 ? 'Caution' : 'Significant'}
</Text>
</View>
</View>
</View>
{/* Individual Dosha Cards */}
<DoshaCard
name="Manglik Dosha"
icon="♂"
iconColor="#DC2626"
result={report.manglik}
info="Mars in houses 1, 2, 4, 7, 8, or 12 creates marital challenges"
/>
<DoshaCard
name="Kalsarpa Dosha"
icon="🐍"
iconColor="#7C3AED"
result={report.kalsarpa}
info="All planets between Rahu-Ketu axis causes obstacles in life"
/>
<DoshaCard
name="Sadhesati"
icon="♄"
iconColor="#1F2937"
result={report.sadhesati}
info="Saturn transit through 12th, 1st, 2nd houses from Moon brings challenges"
/>
{/* Compatibility Note */}
{doshaCount > 0 && (
<View style={styles.noteCard}>
<Text style={styles.noteTitle}>💡 Marriage Compatibility Note</Text>
<Text style={styles.noteText}>
For Manglik individuals, marrying another Manglik person can neutralize
the dosha effects. Consult a qualified astrologer for personalized remedies
and timing considerations.
</Text>
</View>
)}
</ScrollView>
);
}
interface DoshaCardProps {
name: string;
icon: string;
iconColor: string;
result: DoshaResult;
info: string;
}
function DoshaCard({ name, icon, iconColor, result, info }: DoshaCardProps) {
return (
<View style={[
styles.doshaCard,
result.present ? styles.doshaPresent : styles.doshaAbsent
]}>
<View style={styles.doshaHeader}>
<Text style={[styles.doshaIcon, { color: iconColor }]}>{icon}</Text>
<View style={styles.doshaHeaderText}>
<Text style={styles.doshaName}>{name}</Text>
<Text style={styles.doshaInfo}>{info}</Text>
</View>
</View>
<View style={[
styles.doshaStatus,
result.present ? styles.statusPresent : styles.statusAbsent
]}>
<Text style={styles.statusText}>
{result.present ? '⚠️ Present' : '✅ Not Present'}
</Text>
</View>
<Text style={styles.doshaDescription}>{result.description}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F9FAFB',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 40,
},
loadingText: {
marginTop: 16,
fontSize: 14,
color: '#6B7280',
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 40,
},
errorText: {
fontSize: 16,
color: '#DC2626',
textAlign: 'center',
},
summaryCard: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
padding: 20,
margin: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 3,
},
summaryTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#1F2937',
marginBottom: 16,
},
summaryContent: {
gap: 12,
},
summaryItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
},
summaryLabel: {
fontSize: 14,
color: '#6B7280',
},
summaryValue: {
fontSize: 18,
fontWeight: 'bold',
},
summaryGood: {
color: '#10B981',
},
summaryWarning: {
color: '#F59E0B',
},
doshaCard: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
padding: 20,
marginHorizontal: 16,
marginBottom: 16,
borderWidth: 2,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 3,
},
doshaPresent: {
borderColor: '#F59E0B',
backgroundColor: '#FFFBEB',
},
doshaAbsent: {
borderColor: '#10B981',
backgroundColor: '#F0FDF4',
},
doshaHeader: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 16,
},
doshaIcon: {
fontSize: 32,
marginRight: 12,
},
doshaHeaderText: {
flex: 1,
},
doshaName: {
fontSize: 18,
fontWeight: 'bold',
color: '#1F2937',
marginBottom: 4,
},
doshaInfo: {
fontSize: 12,
color: '#6B7280',
lineHeight: 18,
},
doshaStatus: {
paddingVertical: 8,
paddingHorizontal: 16,
borderRadius: 8,
alignSelf: 'flex-start',
marginBottom: 12,
},
statusPresent: {
backgroundColor: '#FEF3C7',
},
statusAbsent: {
backgroundColor: '#D1FAE5',
},
statusText: {
fontSize: 14,
fontWeight: '600',
color: '#1F2937',
},
doshaDescription: {
fontSize: 14,
color: '#4B5563',
lineHeight: 20,
},
noteCard: {
backgroundColor: '#EFF6FF',
borderRadius: 12,
padding: 16,
margin: 16,
borderLeftWidth: 4,
borderLeftColor: '#3B82F6',
},
noteTitle: {
fontSize: 16,
fontWeight: '600',
color: '#1E40AF',
marginBottom: 8,
},
noteText: {
fontSize: 14,
color: '#1E40AF',
lineHeight: 20,
},
});
Step 4: Matrimonial Match Filter Integration
Filter profiles by dosha compatibility:
// services/match-filter.ts
interface UserProfile {
id: string;
name: string;
birthData: BirthData;
doshaReport?: CompleteDoshaReport;
}
interface MatchFilters {
manglikOnly: boolean;
noManglik: boolean;
allowKalsarpa: boolean;
allowSadhesati: boolean;
}
export class MatchFilterService {
async filterProfiles(
profiles: UserProfile[],
userDoshaReport: CompleteDoshaReport,
filters: MatchFilters
): Promise<UserProfile[]> {
const userIsManglik = userDoshaReport.manglik.present;
return profiles.filter(profile => {
// Skip profiles without dosha data
if (!profile.doshaReport) return false;
const profileIsManglik = profile.doshaReport.manglik.present;
const profileHasKalsarpa = profile.doshaReport.kalsarpa.present;
const profileHasSadhesati = profile.doshaReport.sadhesati.present;
// Manglik filtering logic
if (userIsManglik) {
// If user is Manglik and wants Manglik only
if (filters.manglikOnly && !profileIsManglik) return false;
} else {
// If user is not Manglik and doesn't want Manglik
if (filters.noManglik && profileIsManglik) return false;
}
// Kalsarpa filter
if (!filters.allowKalsarpa && profileHasKalsarpa) return false;
// Sadhesati filter
if (!filters.allowSadhesati && profileHasSadhesati) return false;
return true;
});
}
async batchCheckDoshas(
profiles: UserProfile[],
apiKey: string
): Promise<UserProfile[]> {
const service = new DoshaCheckerService(apiKey);
// Process in batches of 10 to avoid rate limits
const batchSize = 10;
const results: UserProfile[] = [];
for (let i = 0; i < profiles.length; i += batchSize) {
const batch = profiles.slice(i, i + batchSize);
const doshaReports = await Promise.all(
batch.map(profile =>
service.checkAllDoshas(profile.birthData)
.catch(error => {
console.error(`Failed to check doshas for ${profile.id}:`, error);
return null;
})
)
);
batch.forEach((profile, index) => {
results.push({
...profile,
doshaReport: doshaReports[index] || undefined,
});
});
// Rate limiting delay
if (i + batchSize < profiles.length) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
return results;
}
}
Step 5: Compatibility Score Integration
Combine dosha check with Gun Milan:
// services/marriage-compatibility.ts
interface CompatibilityResult {
total: number;
maxScore: number;
percentage: number;
isCompatible: boolean;
breakdown: Array<{
category: string;
score: number;
description: string;
}>;
}
interface CompleteMatchAnalysis {
gunMilan: CompatibilityResult;
doshaCompatibility: {
bothManglik: boolean;
manglikMismatch: boolean;
kalsarpaIssues: boolean;
sadhesatiConcerns: boolean;
overallRating: 'Excellent' | 'Good' | 'Fair' | 'Caution';
};
recommendation: string;
}
export class MarriageCompatibilityService {
private doshaService: DoshaCheckerService;
private apiKey: string;
constructor(apiKey: string) {
this.apiKey = apiKey;
this.doshaService = new DoshaCheckerService(apiKey);
}
async analyzeMatch(
person1: BirthData,
person2: BirthData
): Promise<CompleteMatchAnalysis> {
// Fetch Gun Milan score
const gunMilan = await this.getGunMilan(person1, person2);
// Check doshas for both
const [dosha1, dosha2] = await Promise.all([
this.doshaService.checkAllDoshas(person1),
this.doshaService.checkAllDoshas(person2),
]);
// Analyze dosha compatibility
const bothManglik = dosha1.manglik.present && dosha2.manglik.present;
const manglikMismatch = dosha1.manglik.present !== dosha2.manglik.present;
const kalsarpaIssues = dosha1.kalsarpa.present || dosha2.kalsarpa.present;
const sadhesatiConcerns = dosha1.sadhesati.present || dosha2.sadhesati.present;
// Calculate overall rating
let overallRating: 'Excellent' | 'Good' | 'Fair' | 'Caution' = 'Excellent';
if (manglikMismatch && !bothManglik) {
overallRating = 'Caution';
} else if (kalsarpaIssues || sadhesatiConcerns) {
overallRating = gunMilan.total >= 24 ? 'Good' : 'Fair';
} else if (gunMilan.total >= 28) {
overallRating = 'Excellent';
} else if (gunMilan.total >= 20) {
overallRating = 'Good';
} else {
overallRating = 'Fair';
}
// Generate recommendation
const recommendation = this.generateRecommendation(
gunMilan,
{ bothManglik, manglikMismatch, kalsarpaIssues, sadhesatiConcerns }
);
return {
gunMilan,
doshaCompatibility: {
bothManglik,
manglikMismatch,
kalsarpaIssues,
sadhesatiConcerns,
overallRating,
},
recommendation,
};
}
private async getGunMilan(
person1: BirthData,
person2: BirthData
): Promise<CompatibilityResult> {
const response = await fetch(
'https://roxyapi.com/api/v2/vedic-astrology/compatibility',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-API-Key': this.apiKey,
},
body: JSON.stringify({ person1, person2 }),
}
);
if (!response.ok) {
throw new Error('Compatibility check failed');
}
return await response.json();
}
private generateRecommendation(
gunMilan: CompatibilityResult,
doshaStatus: {
bothManglik: boolean;
manglikMismatch: boolean;
kalsarpaIssues: boolean;
sadhesatiConcerns: boolean;
}
): string {
if (doshaStatus.manglikMismatch && !doshaStatus.bothManglik) {
return 'Manglik dosha mismatch detected. Traditional remedies recommended before marriage. Consult qualified astrologer for personalized guidance.';
}
if (doshaStatus.bothManglik) {
return 'Both individuals have Manglik dosha, which neutralizes its effects. Gun Milan score is ' +
gunMilan.total + '/36. ' +
(gunMilan.isCompatible ? 'Good compatibility indicated.' : 'Consider remedies to improve compatibility.');
}
if (gunMilan.total >= 28) {
return 'Excellent compatibility! High Gun Milan score with minimal dosha concerns. Favorable for marriage.';
}
if (gunMilan.total >= 20) {
return 'Good compatibility. Gun Milan score is acceptable. ' +
(doshaStatus.kalsarpaIssues || doshaStatus.sadhesatiConcerns
? 'Some dosha concerns present - remedies may be beneficial.'
: 'No significant dosha issues.');
}
return 'Fair compatibility. Gun Milan score below recommended threshold. Consider astrological remedies and thorough consultation.';
}
}
Step 6: Production Considerations
Caching Strategy
// Cache dosha results for 90 days (doshas don't change with time)
const DOSHA_CACHE_TTL = 90 * 24 * 60 * 60; // 90 days in seconds
async function getCachedDoshaReport(
birthData: BirthData,
redis: Redis
): Promise<CompleteDoshaReport | null> {
const cacheKey = `dosha:${birthData.date}:${birthData.time}:${birthData.latitude}:${birthData.longitude}`;
const cached = await redis.get(cacheKey);
if (cached) {
return JSON.parse(cached);
}
return null;
}
async function cacheDoshaReport(
birthData: BirthData,
report: CompleteDoshaReport,
redis: Redis
): Promise<void> {
const cacheKey = `dosha:${birthData.date}:${birthData.time}:${birthData.latitude}:${birthData.longitude}`;
await redis.setex(
cacheKey,
DOSHA_CACHE_TTL,
JSON.stringify(report)
);
}
Database Schema
-- Store dosha results in user profiles
CREATE TABLE user_doshas (
user_id UUID PRIMARY KEY REFERENCES users(id),
manglik_present BOOLEAN NOT NULL,
kalsarpa_present BOOLEAN NOT NULL,
sadhesati_present BOOLEAN NOT NULL,
manglik_description TEXT,
kalsarpa_description TEXT,
sadhesati_description TEXT,
checked_at TIMESTAMP NOT NULL DEFAULT NOW(),
birth_data JSONB NOT NULL,
CONSTRAINT valid_birth_data CHECK (
birth_data ? 'date' AND
birth_data ? 'time' AND
birth_data ? 'latitude' AND
birth_data ? 'longitude'
)
);
-- Index for match filtering
CREATE INDEX idx_user_doshas_manglik ON user_doshas(manglik_present);
CREATE INDEX idx_user_doshas_filtering ON user_doshas(manglik_present, kalsarpa_present, sadhesati_present);
Error Handling
class DoshaCheckError extends Error {
constructor(
message: string,
public code: 'INVALID_DATA' | 'API_ERROR' | 'RATE_LIMIT' | 'NETWORK_ERROR'
) {
super(message);
this.name = 'DoshaCheckError';
}
}
async function checkDoshaWithRetry(
birthData: BirthData,
maxRetries = 3
): Promise<DoshaResult> {
let lastError: Error | null = null;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await checkManglikDosha(birthData);
} catch (error) {
lastError = error instanceof Error ? error : new Error('Unknown error');
if (attempt < maxRetries) {
// Exponential backoff
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, attempt) * 1000)
);
}
}
}
throw new DoshaCheckError(
`Failed after ${maxRetries} attempts: ${lastError?.message}`,
'API_ERROR'
);
}
Conclusion
You now have a production-ready dosha detection system for matrimonial platforms. The implementation handles all three major doshas (Manglik, Kalsarpa, Sadhesati) with proper UI components, match filtering, and compatibility integration.
Key features implemented:
- Simple boolean + description API responses
- Parallel dosha checking (all three at once)
- Match filtering by dosha compatibility
- Gun Milan integration for complete analysis
- Caching strategy (90-day TTL)
- Batch processing for profile filtering
- Error handling with retries
- Database schema for persistent storage
The APIs return straightforward present: boolean + description: string format, making integration simple. Combine with Gun Milan scores for comprehensive matrimonial matching.
Ready to add dosha detection to your app? Sign up for RoxyAPI and get accurate dosha analysis. Full documentation at roxyapi.com/docs.