RoxyAPI

Menu

Building a Kundli Matching Feature Using Vedic Astrology API

13 min read
By Anika Sharma
Vedic AstrologyKundli MatchingGun MilanMatrimonial AppsCompatibility API

Complete guide to implementing gun milan compatibility scoring for matrimonial apps. Includes Ashtakoot system, dosha checks, and match analysis.

Building a Kundli Matching Feature Using Vedic Astrology API

Kundli matching (gun milan) is the cornerstone of Indian matrimonial platforms. Implementing accurate compatibility scoring requires understanding the Ashtakoot system, checking for doshas, and presenting insights in a user-friendly way. This comprehensive guide shows you how to build production-ready kundli matching features using the RoxyAPI Vedic Astrology API.

By the end of this tutorial, you will have a complete kundli matching system with compatibility scoring, detailed breakdowns, dosha detection, and remedies suggestions.

What We Will Build

Our kundli matching feature includes:

  1. Compatibility Scoring - Ashtakoot gun milan with 36-point system
  2. Detailed Breakdown - Individual scores for 8 compatibility factors
  3. Manglik Dosha Detection - Automated Mars affliction checking
  4. Kalsarpa Dosha Detection - Rahu-Ketu axis analysis
  5. Match Analysis - Strengths, challenges, and recommendations
  6. Remedies System - Suggestions for low compatibility scores
  7. PDF Reports - Downloadable compatibility reports

Understanding Ashtakoot Gun Milan

The Ashtakoot system evaluates compatibility across 8 (Ashta) categories (Koota):

Koota Maximum Points Measures
Varna 1 Spiritual compatibility and ego
Vashya 2 Mutual attraction and control
Tara 3 Health and well-being
Yoni 4 Physical and sexual compatibility
Graha Maitri 5 Mental compatibility and friendship
Gana 6 Temperament and nature
Bhakoot 7 Love, affection, and prosperity
Nadi 8 Health, children, and genetic compatibility

Total Maximum Score: 36 points

Compatibility Interpretation:

  • 28-36 points: Excellent match (80-100%)
  • 21-27 points: Very good match (58-75%)
  • 18-20 points: Average match (50-56%)
  • Below 18 points: Below average, consider remedies

Tech Stack

  • Backend: Node.js/Express or Python/Flask
  • Frontend: React or React Native
  • API: RoxyAPI Vedic Astrology endpoints
  • Database: PostgreSQL or MongoDB
  • File Generation: jsPDF or wkhtmltopdf

Step 1: API Integration

Set up the compatibility endpoint integration:

// lib/compatibility-api.ts
interface BirthProfile {
  name: string;
  date: string;      // YYYY-MM-DD
  time: string;      // HH:MM:SS
  latitude: number;
  longitude: number;
  timezone: number;
  location: string;
}

interface CompatibilityBreakdown {
  category: string;
  score: number;
  description: string;
}

interface CompatibilityResult {
  total: number;
  maxScore: number;
  percentage: number;
  isCompatible: boolean;
  breakdown: CompatibilityBreakdown[];
}

const API_BASE = 'http://localhost:3001/api/v2/vedic-astrology';
const API_KEY = 'your_api_key_here';

export async function calculateCompatibility(
  person1: BirthProfile,
  person2: BirthProfile
): Promise<CompatibilityResult> {
  const response = await fetch(`${API_BASE}/compatibility`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({
      person1: {
        date: person1.date,
        time: person1.time,
        latitude: person1.latitude,
        longitude: person1.longitude,
        timezone: person1.timezone,
      },
      person2: {
        date: person2.date,
        time: person2.time,
        latitude: person2.latitude,
        longitude: person2.longitude,
        timezone: person2.timezone,
      },
    }),
  });

  if (!response.ok) {
    throw new Error(`Compatibility API error: ${response.statusText}`);
  }

  return response.json();
}

Example API Response

{
  "total": 18.5,
  "maxScore": 36,
  "percentage": 51.39,
  "isCompatible": true,
  "breakdown": [
    {
      "category": "Nakshatra (Varna)",
      "score": 0,
      "description": "Spiritual compatibility"
    },
    {
      "category": "Gana",
      "score": 0,
      "description": "Temperament match"
    },
    {
      "category": "Yoni",
      "score": 0,
      "description": "Physical/sexual compatibility"
    },
    {
      "category": "Rashi Lord",
      "score": 3,
      "description": "Mutual affection"
    },
    {
      "category": "Vasya",
      "score": 1,
      "description": "Magnetic control"
    },
    {
      "category": "Tara",
      "score": 1.5,
      "description": "Birth star compatibility"
    },
    {
      "category": "Nadi",
      "score": 8,
      "description": "Health and progeny"
    },
    {
      "category": "Bhakoot",
      "score": 5,
      "description": "Love and prosperity"
    }
  ]
}

Step 2: Dosha Detection

Check for major doshas that affect compatibility:

Manglik Dosha API

// lib/dosha-api.ts
interface ManglikDoshaResult {
  isManglik: boolean;
  severity: 'None' | 'Low' | 'Moderate' | 'High';
  affectedHouses: number[];
  interpretation: string;
  remedies: string[];
}

export async function checkManglikDosha(
  profile: BirthProfile
): Promise<ManglikDoshaResult> {
  const response = await fetch(`${API_BASE}/dosha/manglik`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({
      date: profile.date,
      time: profile.time,
      latitude: profile.latitude,
      longitude: profile.longitude,
      timezone: profile.timezone,
    }),
  });

  if (!response.ok) {
    throw new Error(`Manglik dosha API error: ${response.statusText}`);
  }

  return response.json();
}

Kalsarpa Dosha API

interface KalsarpaDoshaResult {
  hasKalsarpaDosha: boolean;
  type: string | null;
  severity: 'None' | 'Partial' | 'Full';
  interpretation: string;
  remedies: string[];
}

export async function checkKalsarpaDosha(
  profile: BirthProfile
): Promise<KalsarpaDoshaResult> {
  const response = await fetch(`${API_BASE}/dosha/kalsarpa`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({
      date: profile.date,
      time: profile.time,
      latitude: profile.latitude,
      longitude: profile.longitude,
      timezone: profile.timezone,
    }),
  });

  if (!response.ok) {
    throw new Error(`Kalsarpa dosha API error: ${response.statusText}`);
  }

  return response.json();
}

Step 3: Complete Matching Service

Create a comprehensive matching service:

// services/kundli-matching.ts
interface CompleteMatchResult {
  compatibility: CompatibilityResult;
  person1Doshas: {
    manglik: ManglikDoshaResult;
    kalsarpa: KalsarpaDoshaResult;
  };
  person2Doshas: {
    manglik: ManglikDoshaResult;
    kalsarpa: KalsarpaDoshaResult;
  };
  analysis: {
    rating: string;
    strengths: string[];
    challenges: string[];
    recommendations: string[];
  };
}

export class KundliMatchingService {
  async getCompleteMatch(
    person1: BirthProfile,
    person2: BirthProfile
  ): Promise<CompleteMatchResult> {
    // Fetch all data in parallel
    const [compatibility, p1Manglik, p1Kalsarpa, p2Manglik, p2Kalsarpa] =
      await Promise.all([
        calculateCompatibility(person1, person2),
        checkManglikDosha(person1),
        checkKalsarpaDosha(person1),
        checkManglikDosha(person2),
        checkKalsarpaDosha(person2),
      ]);

    // Generate analysis
    const analysis = this.generateAnalysis(
      compatibility,
      { manglik: p1Manglik, kalsarpa: p1Kalsarpa },
      { manglik: p2Manglik, kalsarpa: p2Kalsarpa }
    );

    return {
      compatibility,
      person1Doshas: { manglik: p1Manglik, kalsarpa: p1Kalsarpa },
      person2Doshas: { manglik: p2Manglik, kalsarpa: p2Kalsarpa },
      analysis,
    };
  }

  private generateAnalysis(
    compatibility: CompatibilityResult,
    person1Doshas: any,
    person2Doshas: any
  ) {
    const rating = this.getRating(compatibility.percentage);
    const strengths = this.getStrengths(compatibility.breakdown);
    const challenges = this.getChallenges(compatibility.breakdown);
    const recommendations = this.getRecommendations(
      compatibility,
      person1Doshas,
      person2Doshas
    );

    return { rating, strengths, challenges, recommendations };
  }

  private getRating(percentage: number): string {
    if (percentage >= 80) return 'Excellent';
    if (percentage >= 70) return 'Very Good';
    if (percentage >= 60) return 'Good';
    if (percentage >= 50) return 'Average';
    if (percentage >= 40) return 'Below Average';
    return 'Poor';
  }

  private getStrengths(breakdown: CompatibilityBreakdown[]): string[] {
    return breakdown
      .filter((item) => {
        const maxForCategory = this.getMaxScore(item.category);
        return item.score >= maxForCategory * 0.7;
      })
      .map((item) => {
        const interpretations: Record<string, string> = {
          'Nadi': 'Strong health compatibility and good prospects for children',
          'Bhakoot': 'Excellent love and prosperity potential in marriage',
          'Gana': 'Compatible temperaments leading to harmonious relationship',
          'Yoni': 'Strong physical and intimate compatibility',
          'Rashi Lord': 'Natural affection and understanding between partners',
          'Tara': 'Good health and well-being indicators',
          'Vasya': 'Positive mutual influence and attraction',
          'Nakshatra (Varna)': 'Strong spiritual connection',
        };
        return interpretations[item.category] || `Good ${item.description}`;
      });
  }

  private getChallenges(breakdown: CompatibilityBreakdown[]): string[] {
    return breakdown
      .filter((item) => {
        const maxForCategory = this.getMaxScore(item.category);
        return item.score < maxForCategory * 0.3;
      })
      .map((item) => {
        const interpretations: Record<string, string> = {
          'Nadi': 'Health compatibility needs attention - consider medical consultation',
          'Bhakoot': 'Financial and emotional harmony requires conscious effort',
          'Gana': 'Different temperaments - patience and understanding crucial',
          'Yoni': 'Physical compatibility challenges - open communication important',
          'Rashi Lord': 'Building mutual affection requires dedicated effort',
          'Tara': 'Health and well-being need special care',
          'Vasya': 'Control dynamics need balance',
          'Nakshatra (Varna)': 'Spiritual goals may differ - find common ground',
        };
        return interpretations[item.category] || `Challenge in ${item.description}`;
      });
  }

  private getRecommendations(
    compatibility: CompatibilityResult,
    person1Doshas: any,
    person2Doshas: any
  ): string[] {
    const recommendations: string[] = [];

    // Low compatibility recommendations
    if (compatibility.percentage < 50) {
      recommendations.push(
        'Consider pre-marital counseling to understand and address compatibility challenges'
      );
      recommendations.push(
        'Perform Navagraha puja to strengthen planetary positions'
      );
    }

    // Manglik dosha recommendations
    if (person1Doshas.manglik.isManglik || person2Doshas.manglik.isManglik) {
      if (person1Doshas.manglik.isManglik && person2Doshas.manglik.isManglik) {
        recommendations.push(
          'Both partners have Manglik dosha - this neutralizes the effect'
        );
      } else {
        recommendations.push(
          'One partner has Manglik dosha - consider Kumbh Vivah or gemstone remedies'
        );
        recommendations.push('Perform Mangal Shanti puja before marriage');
      }
    }

    // Kalsarpa dosha recommendations
    if (
      person1Doshas.kalsarpa.hasKalsarpaDosha ||
      person2Doshas.kalsarpa.hasKalsarpaDosha
    ) {
      recommendations.push('Perform Rahu-Ketu Shanti puja');
      recommendations.push('Visit Kalsarpa dosha temples in Trimbakeshwar');
    }

    // Low Nadi score (critical)
    const nadiScore =
      compatibility.breakdown.find((b) => b.category === 'Nadi')?.score || 0;
    if (nadiScore === 0) {
      recommendations.push(
        'CRITICAL: Nadi dosha present - consult experienced astrologer before proceeding'
      );
      recommendations.push(
        'Consider medical tests for genetic compatibility'
      );
    }

    // Good compatibility encouragement
    if (compatibility.percentage >= 70) {
      recommendations.push(
        'Excellent match! Choose an auspicious muhurat for marriage'
      );
      recommendations.push(
        'Perform simple puja on wedding day for blessings'
      );
    }

    return recommendations;
  }

  private getMaxScore(category: string): number {
    const maxScores: Record<string, number> = {
      'Nakshatra (Varna)': 1,
      'Vasya': 2,
      'Tara': 3,
      'Yoni': 4,
      'Rashi Lord': 5,
      'Gana': 6,
      'Bhakoot': 7,
      'Nadi': 8,
    };
    return maxScores[category] || 1;
  }
}

Step 4: React Component for Match Display

Create an intuitive UI for match results:

// components/MatchingResults.tsx
import React from 'react';
import { View, Text, ScrollView, StyleSheet } from 'react-native';

interface Props {
  matchResult: CompleteMatchResult;
  person1Name: string;
  person2Name: string;
}

export function MatchingResults({ matchResult, person1Name, person2Name }: Props) {
  const { compatibility, person1Doshas, person2Doshas, analysis } = matchResult;

  const getScoreColor = (score: number, maxScore: number): string => {
    const percentage = (score / maxScore) * 100;
    if (percentage >= 70) return '#10B981'; // Green
    if (percentage >= 40) return '#F59E0B'; // Yellow
    return '#EF4444'; // Red
  };

  return (
    <ScrollView style={styles.container}>
      {/* Overall Score */}
      <View style={styles.scoreCard}>
        <Text style={styles.scoreTitle}>Overall Compatibility</Text>
        <View style={styles.scoreCircle}>
          <Text style={styles.scoreValue}>{compatibility.total}</Text>
          <Text style={styles.scoreMax}>/ {compatibility.maxScore}</Text>
        </View>
        <Text style={styles.scorePercentage}>
          {compatibility.percentage.toFixed(1)}%
        </Text>
        <View
          style={[
            styles.ratingBadge,
            {
              backgroundColor:
                analysis.rating === 'Excellent' || analysis.rating === 'Very Good'
                  ? '#10B981'
                  : analysis.rating === 'Good' || analysis.rating === 'Average'
                  ? '#F59E0B'
                  : '#EF4444',
            },
          ]}
        >
          <Text style={styles.ratingText}>{analysis.rating} Match</Text>
        </View>
      </View>

      {/* Detailed Breakdown */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>📊 Detailed Breakdown</Text>
        {compatibility.breakdown.map((item, index) => {
          const maxScore = service.getMaxScore(item.category);
          return (
            <View key={index} style={styles.breakdownItem}>
              <View style={styles.breakdownHeader}>
                <Text style={styles.breakdownCategory}>{item.category}</Text>
                <Text
                  style={[
                    styles.breakdownScore,
                    { color: getScoreColor(item.score, maxScore) },
                  ]}
                >
                  {item.score} / {maxScore}
                </Text>
              </View>
              <Text style={styles.breakdownDescription}>{item.description}</Text>
              <View style={styles.progressBar}>
                <View
                  style={[
                    styles.progressFill,
                    {
                      width: `${(item.score / maxScore) * 100}%`,
                      backgroundColor: getScoreColor(item.score, maxScore),
                    },
                  ]}
                />
              </View>
            </View>
          );
        })}
      </View>

      {/* Dosha Check */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>🔮 Dosha Analysis</Text>

        <View style={styles.doshaCard}>
          <Text style={styles.doshaTitle}>{person1Name}</Text>
          <View style={styles.doshaRow}>
            <Text style={styles.doshaLabel}>Manglik Dosha:</Text>
            <View
              style={[
                styles.doshaBadge,
                {
                  backgroundColor: person1Doshas.manglik.isManglik
                    ? '#FEF3C7'
                    : '#D1FAE5',
                },
              ]}
            >
              <Text
                style={[
                  styles.doshaValue,
                  {
                    color: person1Doshas.manglik.isManglik ? '#92400E' : '#065F46',
                  },
                ]}
              >
                {person1Doshas.manglik.isManglik ? 'Yes' : 'No'}
              </Text>
            </View>
          </View>
          <View style={styles.doshaRow}>
            <Text style={styles.doshaLabel}>Kalsarpa Dosha:</Text>
            <View
              style={[
                styles.doshaBadge,
                {
                  backgroundColor: person1Doshas.kalsarpa.hasKalsarpaDosha
                    ? '#FEF3C7'
                    : '#D1FAE5',
                },
              ]}
            >
              <Text
                style={[
                  styles.doshaValue,
                  {
                    color: person1Doshas.kalsarpa.hasKalsarpaDosha
                      ? '#92400E'
                      : '#065F46',
                  },
                ]}
              >
                {person1Doshas.kalsarpa.hasKalsarpaDosha ? 'Yes' : 'No'}
              </Text>
            </View>
          </View>
        </View>

        <View style={styles.doshaCard}>
          <Text style={styles.doshaTitle}>{person2Name}</Text>
          <View style={styles.doshaRow}>
            <Text style={styles.doshaLabel}>Manglik Dosha:</Text>
            <View
              style={[
                styles.doshaBadge,
                {
                  backgroundColor: person2Doshas.manglik.isManglik
                    ? '#FEF3C7'
                    : '#D1FAE5',
                },
              ]}
            >
              <Text
                style={[
                  styles.doshaValue,
                  {
                    color: person2Doshas.manglik.isManglik ? '#92400E' : '#065F46',
                  },
                ]}
              >
                {person2Doshas.manglik.isManglik ? 'Yes' : 'No'}
              </Text>
            </View>
          </View>
          <View style={styles.doshaRow}>
            <Text style={styles.doshaLabel}>Kalsarpa Dosha:</Text>
            <View
              style={[
                styles.doshaBadge,
                {
                  backgroundColor: person2Doshas.kalsarpa.hasKalsarpaDosha
                    ? '#FEF3C7'
                    : '#D1FAE5',
                },
              ]}
            >
              <Text
                style={[
                  styles.doshaValue,
                  {
                    color: person2Doshas.kalsarpa.hasKalsarpaDosha
                      ? '#92400E'
                      : '#065F46',
                  },
                ]}
              >
                {person2Doshas.kalsarpa.hasKalsarpaDosha ? 'Yes' : 'No'}
              </Text>
            </View>
          </View>
        </View>
      </View>

      {/* Strengths */}
      {analysis.strengths.length > 0 && (
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>✨ Strengths</Text>
          {analysis.strengths.map((strength, index) => (
            <View key={index} style={styles.listItem}>
              <Text style={styles.bullet}>•</Text>
              <Text style={styles.listText}>{strength}</Text>
            </View>
          ))}
        </View>
      )}

      {/* Challenges */}
      {analysis.challenges.length > 0 && (
        <View style={styles.section}>
          <Text style={styles.sectionTitle}>⚠️ Areas of Attention</Text>
          {analysis.challenges.map((challenge, index) => (
            <View key={index} style={styles.listItem}>
              <Text style={styles.bullet}>•</Text>
              <Text style={styles.listText}>{challenge}</Text>
            </View>
          ))}
        </View>
      )}

      {/* Recommendations */}
      <View style={styles.section}>
        <Text style={styles.sectionTitle}>💡 Recommendations</Text>
        {analysis.recommendations.map((recommendation, index) => (
          <View
            key={index}
            style={[
              styles.recommendationCard,
              recommendation.includes('CRITICAL') && styles.criticalCard,
            ]}
          >
            <Text style={styles.recommendationText}>{recommendation}</Text>
          </View>
        ))}
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFF8F0',
  },
  scoreCard: {
    backgroundColor: '#FFFFFF',
    margin: 16,
    borderRadius: 20,
    padding: 24,
    alignItems: 'center',
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 4 },
    shadowOpacity: 0.1,
    shadowRadius: 12,
    elevation: 6,
  },
  scoreTitle: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#8B4513',
    marginBottom: 16,
  },
  scoreCircle: {
    flexDirection: 'row',
    alignItems: 'baseline',
  },
  scoreValue: {
    fontSize: 64,
    fontWeight: 'bold',
    color: '#CD853F',
  },
  scoreMax: {
    fontSize: 24,
    color: '#A0522D',
    marginLeft: 4,
  },
  scorePercentage: {
    fontSize: 20,
    color: '#8B4513',
    marginTop: 8,
  },
  ratingBadge: {
    marginTop: 16,
    paddingHorizontal: 20,
    paddingVertical: 8,
    borderRadius: 20,
  },
  ratingText: {
    color: '#FFFFFF',
    fontWeight: 'bold',
    fontSize: 16,
  },
  section: {
    backgroundColor: '#FFFFFF',
    marginHorizontal: 16,
    marginBottom: 16,
    borderRadius: 16,
    padding: 20,
  },
  sectionTitle: {
    fontSize: 20,
    fontWeight: 'bold',
    color: '#8B4513',
    marginBottom: 16,
  },
  breakdownItem: {
    marginBottom: 20,
  },
  breakdownHeader: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 4,
  },
  breakdownCategory: {
    fontSize: 16,
    fontWeight: '600',
    color: '#4A4A4A',
  },
  breakdownScore: {
    fontSize: 16,
    fontWeight: 'bold',
  },
  breakdownDescription: {
    fontSize: 13,
    color: '#6B7280',
    marginBottom: 8,
  },
  progressBar: {
    height: 8,
    backgroundColor: '#F3F4F6',
    borderRadius: 4,
    overflow: 'hidden',
  },
  progressFill: {
    height: '100%',
    borderRadius: 4,
  },
  doshaCard: {
    backgroundColor: '#F9FAFB',
    borderRadius: 12,
    padding: 16,
    marginBottom: 12,
  },
  doshaTitle: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#8B4513',
    marginBottom: 12,
  },
  doshaRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 8,
  },
  doshaLabel: {
    fontSize: 14,
    color: '#4A4A4A',
  },
  doshaBadge: {
    paddingHorizontal: 12,
    paddingVertical: 4,
    borderRadius: 8,
  },
  doshaValue: {
    fontSize: 14,
    fontWeight: '600',
  },
  listItem: {
    flexDirection: 'row',
    marginBottom: 12,
  },
  bullet: {
    fontSize: 16,
    color: '#CD853F',
    marginRight: 8,
    marginTop: 2,
  },
  listText: {
    flex: 1,
    fontSize: 14,
    color: '#4A4A4A',
    lineHeight: 20,
  },
  recommendationCard: {
    backgroundColor: '#EEF2FF',
    borderLeftWidth: 4,
    borderLeftColor: '#6366F1',
    borderRadius: 8,
    padding: 12,
    marginBottom: 12,
  },
  criticalCard: {
    backgroundColor: '#FEF2F2',
    borderLeftColor: '#EF4444',
  },
  recommendationText: {
    fontSize: 14,
    color: '#1F2937',
    lineHeight: 20,
  },
});

Step 5: Database Schema

Store matching results for retrieval:

-- PostgreSQL schema
CREATE TABLE kundli_matches (
  id SERIAL PRIMARY KEY,
  user_id INTEGER NOT NULL,
  person1_name VARCHAR(100) NOT NULL,
  person1_birth_data JSONB NOT NULL,
  person2_name VARCHAR(100) NOT NULL,
  person2_birth_data JSONB NOT NULL,
  compatibility_score DECIMAL(5,2) NOT NULL,
  compatibility_data JSONB NOT NULL,
  dosha_data JSONB NOT NULL,
  analysis_data JSONB NOT NULL,
  created_at TIMESTAMP DEFAULT NOW(),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE INDEX idx_kundli_matches_user ON kundli_matches(user_id);
CREATE INDEX idx_kundli_matches_score ON kundli_matches(compatibility_score DESC);

Step 6: PDF Report Generation

Create downloadable compatibility reports:

// services/pdf-generator.ts
import jsPDF from 'jspdf';

export class MatchingReportGenerator {
  generatePDF(matchResult: CompleteMatchResult, person1: BirthProfile, person2: BirthProfile): Blob {
    const doc = new jsPDF();
    let yPos = 20;

    // Header
    doc.setFontSize(22);
    doc.setTextColor(139, 69, 19);
    doc.text('Kundli Matching Report', 105, yPos, { align: 'center' });
    yPos += 15;

    // Couple names
    doc.setFontSize(16);
    doc.text(`${person1.name} & ${person2.name}`, 105, yPos, { align: 'center' });
    yPos += 15;

    // Overall score
    doc.setFontSize(14);
    doc.text(`Overall Compatibility: ${matchResult.compatibility.total}/36`, 20, yPos);
    yPos += 8;
    doc.text(`Percentage: ${matchResult.compatibility.percentage.toFixed(1)}%`, 20, yPos);
    yPos += 8;
    doc.text(`Rating: ${matchResult.analysis.rating}`, 20, yPos);
    yPos += 15;

    // Detailed breakdown
    doc.setFontSize(12);
    doc.text('Detailed Breakdown:', 20, yPos);
    yPos += 8;

    matchResult.compatibility.breakdown.forEach((item) => {
      doc.setFontSize(10);
      doc.text(`${item.category}: ${item.score} points`, 25, yPos);
      yPos += 6;
    });

    // Add more sections...

    return doc.output('blob');
  }
}

Production Considerations

1. Caching Strategy

Cache compatibility results (they never change for same birth details):

const cacheKey = `compat:${person1.date}:${person1.time}:${person2.date}:${person2.time}`;
const cached = await redis.get(cacheKey);
if (cached) return JSON.parse(cached);

const result = await calculateCompatibility(person1, person2);
await redis.set(cacheKey, JSON.stringify(result), 'EX', 86400 * 30); // 30 days

2. Rate Limiting

Prevent abuse with user-level rate limits:

const key = `ratelimit:${userId}:matching`;
const count = await redis.incr(key);
if (count === 1) await redis.expire(key, 3600); // 1 hour window
if (count > 10) throw new Error('Rate limit exceeded');

3. Premium Features

Monetize with premium matching features:

  • Detailed remedies and gemstone recommendations
  • Priority support from astrologer
  • Extended compatibility reports
  • Muhurat suggestions for marriage
  • Transit analysis for wedding planning

Conclusion

You now have a complete kundli matching system ready for production. The RoxyAPI Vedic Astrology API handles all complex calculations, allowing you to focus on creating exceptional user experiences for matrimonial platforms.

Key features implemented:

  • Ashtakoot gun milan scoring
  • Detailed compatibility breakdown
  • Dosha detection (Manglik, Kalsarpa)
  • Intelligent recommendations
  • PDF report generation

Ready to add kundli matching to your platform? Sign up for RoxyAPI and start building today. Complete API documentation at roxyapi.com/docs.