RoxyAPI

Menu

How to Add Numerology Features to Your Dating App (Complete API Guide)

14 min read
By Marcus Thompson
NumerologyDating AppsMatchmakingAPI IntegrationCompatibility

Build numerology-powered matchmaking features with compatibility scoring, personality insights, and relationship forecasts. Complete API integration guide for dating apps.

How to Add Numerology Features to Your Dating App (Complete API Guide)

Dating apps are constantly looking for innovative ways to help users find meaningful connections. While swiping and algorithms work, numerology compatibility adds a unique, personalized dimension that sets your platform apart. In this comprehensive guide, you will learn how to integrate numerology-based matchmaking features using the RoxyAPI Numerology API.

Why Numerology for Dating Apps?

Numerology compatibility analysis provides users with:

  • Personality-based matching beyond photos and bios
  • Relationship insights that go deeper than basic interests
  • Compatibility scores that explain why two people might click
  • Conversation starters rooted in birth dates and names
  • Differentiation from competitors using standard algorithms

Apps like Co-Star (astrology) have proven that users love personalized cosmic insights. Numerology offers similar benefits with simpler data requirements: just birth dates and names.

What We Will Build

In this tutorial, you will build three core numerology features for a dating app:

  1. Compatibility Scoring - Calculate match percentages between two users
  2. Personality Profiles - Generate detailed numerology profiles for user profiles
  3. Relationship Insights - Provide strengths, challenges, and advice for matches

All features use the RoxyAPI Numerology API with production-ready endpoints.

Prerequisites

Before starting, ensure you have:

  • Node.js 18+ or Bun runtime installed
  • RoxyAPI account with API key (sign up at roxyapi.com)
  • Existing dating app with user profiles (birth dates required)
  • React Native or Next.js project (examples work for both)

Step 1: Setting Up the Numerology API Client

First, create a reusable API client for all numerology requests. This handles authentication and error handling.

// lib/numerology-client.ts
const NUMEROLOGY_API = 'https://roxyapi.com/api/v2/numerology';
const API_KEY = process.env.ROXYAPI_KEY!;

export async function calculateCompatibility(
  person1: { lifePath: number; expression: number; soulUrge: number },
  person2: { lifePath: number; expression: number; soulUrge: number }
) {
  const response = await fetch(`${NUMEROLOGY_API}/compatibility`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({ person1, person2 }),
  });

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

  return response.json();
}

export async function generateChart(birthDate: string, fullName: string) {
  const response = await fetch(`${NUMEROLOGY_API}/chart`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({ birthDate, fullName }),
  });

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

  return response.json();
}

export async function calculateLifePath(birthDate: string) {
  const response = await fetch(`${NUMEROLOGY_API}/life-path`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({ birthDate }),
  });

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

  return response.json();
}

Store your API key in environment variables:

# .env.local
ROXYAPI_KEY=your_api_key_here

Step 2: Building Compatibility Scoring

The compatibility endpoint calculates match percentages between two users. It requires Life Path, Expression, and Soul Urge numbers for both people.

Calculating Core Numbers

First, generate each user's core numbers when they complete their profile:

// lib/user-numerology.ts
import { generateChart } from './numerology-client';

export async function calculateUserNumerology(
  userId: string,
  birthDate: string,
  fullName: string
) {
  try {
    const chart = await generateChart(birthDate, fullName);

    // Store in database for reuse
    await prisma.userNumerology.upsert({
      where: { userId },
      create: {
        userId,
        lifePath: chart.lifePath.number,
        expression: chart.expression.number,
        soulUrge: chart.soulUrge.number,
        personality: chart.personality.number,
        fullChart: chart, // Store complete chart for profile display
      },
      update: {
        lifePath: chart.lifePath.number,
        expression: chart.expression.number,
        soulUrge: chart.soulUrge.number,
        personality: chart.personality.number,
        fullChart: chart,
      },
    });

    return chart;
  } catch (error) {
    console.error('Failed to calculate numerology:', error);
    throw error;
  }
}

Implementing Match Percentage

Now calculate compatibility when users view potential matches:

// lib/match-compatibility.ts
import { calculateCompatibility } from './numerology-client';

export async function getMatchCompatibility(userId: string, targetUserId: string) {
  // Fetch numerology data for both users
  const [user, target] = await Promise.all([
    prisma.userNumerology.findUnique({ where: { userId } }),
    prisma.userNumerology.findUnique({ where: { userId: targetUserId } }),
  ]);

  if (!user || !target) {
    throw new Error('Numerology data not found for users');
  }

  const compatibility = await calculateCompatibility(
    {
      lifePath: user.lifePath,
      expression: user.expression,
      soulUrge: user.soulUrge,
    },
    {
      lifePath: target.lifePath,
      expression: target.expression,
      soulUrge: target.soulUrge,
    }
  );

  return compatibility;
}

Example API Response

The compatibility endpoint returns detailed analysis:

{
  "overallScore": 82,
  "rating": "Excellent",
  "lifePath": {
    "person1": 5,
    "person2": 3,
    "compatibility": 85,
    "description": "5 and 3 create an exciting, dynamic partnership. Both value freedom and variety, with 5 bringing adventure and 3 adding creativity and social charm. This combination thrives on spontaneity and new experiences."
  },
  "expression": {
    "person1": 7,
    "person2": 9,
    "compatibility": 78,
    "description": "7 and 9 form a spiritually aligned connection. 7 provides depth and introspection while 9 brings compassion and humanitarian vision. Together they create meaningful relationships focused on growth."
  },
  "soulUrge": {
    "person1": 6,
    "person2": 2,
    "compatibility": 90,
    "description": "6 and 2 share exceptional emotional compatibility. Both prioritize harmony, nurturing, and relationships. This combination creates stable, supportive partnerships built on mutual care."
  },
  "strengths": [
    "Both value freedom and new experiences",
    "Excellent social chemistry and communication",
    "Shared appreciation for creativity and adventure",
    "Strong emotional connection and mutual support"
  ],
  "challenges": [
    "5 may feel restrained by 3's need for attention",
    "7's need for solitude can conflict with social energy",
    "Balancing independence with togetherness requires effort"
  ],
  "advice": "Embrace your shared love for adventure while respecting each person's unique needs. Schedule regular quality time together and individual space for personal growth. Use your natural communication skills to address conflicts early."
}

Step 3: Displaying Compatibility in Your UI

Create a match card component that shows compatibility scores:

// components/MatchCard.tsx
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';

interface MatchCardProps {
  userId: string;
  targetUserId: string;
}

export function MatchCard({ userId, targetUserId }: MatchCardProps) {
  const [compatibility, setCompatibility] = useState<any>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchCompatibility() {
      try {
        const response = await fetch('/api/match-compatibility', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ userId, targetUserId }),
        });
        const data = await response.json();
        setCompatibility(data);
      } catch (error) {
        console.error('Failed to fetch compatibility:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchCompatibility();
  }, [userId, targetUserId]);

  if (loading) {
    return <ActivityIndicator size="large" color="#6B46C1" />;
  }

  if (!compatibility) {
    return null;
  }

  const getScoreColor = (score: number) => {
    if (score >= 80) return '#10B981'; // Green
    if (score >= 60) return '#F59E0B'; // Yellow
    return '#EF4444'; // Red
  };

  return (
    <View style={styles.container}>
      <View style={styles.scoreContainer}>
        <Text style={[styles.score, { color: getScoreColor(compatibility.overallScore) }]}>
          {compatibility.overallScore}%
        </Text>
        <Text style={styles.rating}>{compatibility.rating} Match</Text>
      </View>

      <View style={styles.aspectsContainer}>
        <AspectScore
          label="Life Path"
          score={compatibility.lifePath.compatibility}
          icon="🎯"
        />
        <AspectScore
          label="Expression"
          score={compatibility.expression.compatibility}
          icon="✨"
        />
        <AspectScore
          label="Soul Urge"
          score={compatibility.soulUrge.compatibility}
          icon="💜"
        />
      </View>

      <View style={styles.strengthsContainer}>
        <Text style={styles.sectionTitle}>Strengths</Text>
        {compatibility.strengths.slice(0, 3).map((strength: string, index: number) => (
          <Text key={index} style={styles.bulletPoint}>
            • {strength}
          </Text>
        ))}
      </View>

      <Text style={styles.advice}>{compatibility.advice}</Text>
    </View>
  );
}

function AspectScore({ label, score, icon }: { label: string; score: number; icon: string }) {
  return (
    <View style={styles.aspect}>
      <Text style={styles.icon}>{icon}</Text>
      <Text style={styles.aspectLabel}>{label}</Text>
      <Text style={styles.aspectScore}>{score}%</Text>
    </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,
  },
  scoreContainer: {
    alignItems: 'center',
    marginBottom: 20,
  },
  score: {
    fontSize: 48,
    fontWeight: 'bold',
  },
  rating: {
    fontSize: 18,
    color: '#6B7280',
    marginTop: 5,
  },
  aspectsContainer: {
    flexDirection: 'row',
    justifyContent: 'space-around',
    marginBottom: 20,
    paddingVertical: 15,
    backgroundColor: '#F9FAFB',
    borderRadius: 12,
  },
  aspect: {
    alignItems: 'center',
  },
  icon: {
    fontSize: 24,
    marginBottom: 5,
  },
  aspectLabel: {
    fontSize: 12,
    color: '#6B7280',
    marginBottom: 3,
  },
  aspectScore: {
    fontSize: 16,
    fontWeight: '600',
    color: '#111827',
  },
  strengthsContainer: {
    marginBottom: 15,
  },
  sectionTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#111827',
    marginBottom: 10,
  },
  bulletPoint: {
    fontSize: 14,
    color: '#4B5563',
    marginBottom: 5,
    lineHeight: 20,
  },
  advice: {
    fontSize: 14,
    color: '#6B46C1',
    fontStyle: 'italic',
    marginTop: 10,
    lineHeight: 20,
  },
});

Step 4: Building User Numerology Profiles

Generate complete numerology profiles for user profile pages using the /chart endpoint:

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

interface NumerologyProfileProps {
  userId: string;
}

export function NumerologyProfile({ userId }: NumerologyProfileProps) {
  const [chart, setChart] = useState<any>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchChart() {
      try {
        const response = await fetch(`/api/users/${userId}/numerology`);
        const data = await response.json();
        setChart(data);
      } catch (error) {
        console.error('Failed to fetch numerology chart:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchChart();
  }, [userId]);

  if (loading || !chart) {
    return null;
  }

  return (
    <ScrollView style={styles.container}>
      <NumberCard
        title="Life Path"
        number={chart.lifePath.number}
        keywords={chart.lifePath.meaning.keywords}
        description={chart.lifePath.meaning.description}
        icon="🎯"
      />

      <NumberCard
        title="Expression"
        number={chart.expression.number}
        keywords={chart.expression.meaning.keywords}
        description={chart.expression.meaning.description}
        icon="✨"
      />

      <NumberCard
        title="Soul Urge"
        number={chart.soulUrge.number}
        keywords={chart.soulUrge.meaning.keywords}
        description={chart.soulUrge.meaning.description}
        icon="💜"
      />

      <View style={styles.summaryCard}>
        <Text style={styles.summaryTitle}>Holistic Summary</Text>
        <Text style={styles.summaryText}>{chart.summary}</Text>
      </View>

      {chart.karmicLessons.lessons.length > 0 && (
        <View style={styles.karmicCard}>
          <Text style={styles.karmicTitle}>Areas for Growth</Text>
          {chart.karmicLessons.lessons.map((lesson: any, index: number) => (
            <View key={index} style={styles.lessonItem}>
              <Text style={styles.lessonNumber}>Number {lesson.number}</Text>
              <Text style={styles.lessonDescription}>{lesson.description}</Text>
            </View>
          ))}
        </View>
      )}
    </ScrollView>
  );
}

function NumberCard({
  title,
  number,
  keywords,
  description,
  icon,
}: {
  title: string;
  number: number;
  keywords: string[];
  description: string;
  icon: string;
}) {
  return (
    <View style={styles.card}>
      <View style={styles.cardHeader}>
        <Text style={styles.cardIcon}>{icon}</Text>
        <View>
          <Text style={styles.cardTitle}>{title}</Text>
          <Text style={styles.cardNumber}>Number {number}</Text>
        </View>
      </View>

      <View style={styles.keywordsContainer}>
        {keywords.slice(0, 4).map((keyword, index) => (
          <View key={index} style={styles.keywordBadge}>
            <Text style={styles.keywordText}>{keyword}</Text>
          </View>
        ))}
      </View>

      <Text style={styles.cardDescription}>{description}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F9FAFB',
  },
  card: {
    backgroundColor: '#FFFFFF',
    borderRadius: 16,
    padding: 20,
    marginHorizontal: 15,
    marginVertical: 10,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 8,
    elevation: 3,
  },
  cardHeader: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 15,
  },
  cardIcon: {
    fontSize: 32,
    marginRight: 12,
  },
  cardTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#111827',
  },
  cardNumber: {
    fontSize: 14,
    color: '#6B7280',
    marginTop: 2,
  },
  keywordsContainer: {
    flexDirection: 'row',
    flexWrap: 'wrap',
    marginBottom: 12,
    gap: 8,
  },
  keywordBadge: {
    backgroundColor: '#EDE9FE',
    paddingHorizontal: 12,
    paddingVertical: 6,
    borderRadius: 20,
  },
  keywordText: {
    fontSize: 12,
    color: '#6B46C1',
    fontWeight: '500',
  },
  cardDescription: {
    fontSize: 14,
    color: '#4B5563',
    lineHeight: 22,
  },
  summaryCard: {
    backgroundColor: '#FEF3C7',
    borderRadius: 16,
    padding: 20,
    marginHorizontal: 15,
    marginVertical: 10,
  },
  summaryTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#92400E',
    marginBottom: 10,
  },
  summaryText: {
    fontSize: 14,
    color: '#78350F',
    lineHeight: 22,
  },
  karmicCard: {
    backgroundColor: '#DBEAFE',
    borderRadius: 16,
    padding: 20,
    marginHorizontal: 15,
    marginVertical: 10,
  },
  karmicTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#1E40AF',
    marginBottom: 15,
  },
  lessonItem: {
    marginBottom: 12,
  },
  lessonNumber: {
    fontSize: 14,
    fontWeight: '600',
    color: '#1E40AF',
    marginBottom: 4,
  },
  lessonDescription: {
    fontSize: 13,
    color: '#1E3A8A',
    lineHeight: 20,
  },
});

Step 5: Optimizing Performance with Caching

Numerology calculations are deterministic (same inputs = same outputs), making them perfect for caching:

// lib/cache.ts
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_URL!,
  token: process.env.UPSTASH_REDIS_TOKEN!,
});

const CACHE_TTL = 60 * 60 * 24 * 30; // 30 days

export async function getCachedCompatibility(
  user1Id: string,
  user2Id: string
): Promise<any | null> {
  const key = `compat:${[user1Id, user2Id].sort().join(':')}`;
  const cached = await redis.get(key);
  return cached;
}

export async function setCachedCompatibility(
  user1Id: string,
  user2Id: string,
  data: any
): Promise<void> {
  const key = `compat:${[user1Id, user2Id].sort().join(':')}`;
  await redis.setex(key, CACHE_TTL, JSON.stringify(data));
}

export async function getCachedChart(userId: string): Promise<any | null> {
  const key = `chart:${userId}`;
  const cached = await redis.get(key);
  return cached;
}

export async function setCachedChart(userId: string, data: any): Promise<void> {
  const key = `chart:${userId}`;
  await redis.setex(key, CACHE_TTL, JSON.stringify(data));
}

Update your API routes to use caching:

// pages/api/match-compatibility.ts
import { getCachedCompatibility, setCachedCompatibility } from '@/lib/cache';
import { getMatchCompatibility } from '@/lib/match-compatibility';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
  const { userId, targetUserId } = req.body;

  // Check cache first
  let compatibility = await getCachedCompatibility(userId, targetUserId);

  if (!compatibility) {
    // Calculate and cache
    compatibility = await getMatchCompatibility(userId, targetUserId);
    await setCachedCompatibility(userId, targetUserId, compatibility);
  }

  res.json(compatibility);
}

Step 6: Advanced Features

Daily Horoscope-Style Messages

Use Personal Year calculations for daily insights:

// lib/daily-insight.ts
import { calculatePersonalYear } from './numerology-client';

export async function getDailyInsight(userId: string) {
  const user = await prisma.user.findUnique({ where: { id: userId } });

  if (!user?.birthDate) {
    return null;
  }

  const personalYear = await calculatePersonalYear(user.birthDate);

  const insights = {
    1: 'Today is perfect for starting new projects and taking initiative in relationships.',
    2: 'Focus on cooperation and listening to your partner today. Patience brings rewards.',
    3: 'Express yourself creatively today. Your charm and communication are at their peak.',
    4: 'Build stability in your relationships today. Focus on trust and consistency.',
    5: 'Embrace spontaneity today. Try something new with your partner or matches.',
    6: 'Today is ideal for deepening emotional connections and showing care.',
    7: 'Take time for introspection today. Quality over quantity in connections.',
    8: 'Today brings opportunities for ambitious partnerships. Think long-term.',
    9: 'Practice compassion and forgiveness today. Let go of what does not serve you.',
  };

  return {
    personalYear: personalYear.number,
    insight: insights[personalYear.number as keyof typeof insights],
    forecast: personalYear.meaning.description,
  };
}

Match Suggestions Based on Compatibility

Pre-calculate compatibility scores for batch recommendations:

// jobs/calculate-match-scores.ts
export async function calculateMatchScoresForUser(userId: string) {
  const user = await prisma.userNumerology.findUnique({ where: { userId } });

  if (!user) return;

  // Get all potential matches (apply filters: age, location, preferences)
  const potentialMatches = await prisma.userNumerology.findMany({
    where: {
      userId: { not: userId },
      // Add your filtering logic here
    },
    take: 100, // Process in batches
  });

  const scores = await Promise.all(
    potentialMatches.map(async (match) => {
      const compatibility = await calculateCompatibility(
        {
          lifePath: user.lifePath,
          expression: user.expression,
          soulUrge: user.soulUrge,
        },
        {
          lifePath: match.lifePath,
          expression: match.expression,
          soulUrge: match.soulUrge,
        }
      );

      return {
        userId,
        targetUserId: match.userId,
        score: compatibility.overallScore,
        rating: compatibility.rating,
      };
    })
  );

  // Store scores for fast retrieval
  await prisma.matchScore.createMany({
    data: scores,
    skipDuplicates: true,
  });
}

Monetization Strategies

Numerology features work well as premium add-ons:

  1. Freemium Model: Show basic compatibility scores for free, full reports for premium
  2. Credits System: Users purchase credits for detailed compatibility analysis
  3. Subscription Tiers: Include numerology insights in premium memberships
  4. Boost Features: Highlight users with high numerology compatibility

Testing Your Integration

Test compatibility calculations with known number combinations:

// __tests__/compatibility.test.ts
import { calculateCompatibility } from '@/lib/numerology-client';

describe('Numerology Compatibility', () => {
  it('should calculate high compatibility for harmonious numbers', async () => {
    const result = await calculateCompatibility(
      { lifePath: 2, expression: 6, soulUrge: 9 },
      { lifePath: 6, expression: 2, soulUrge: 9 }
    );

    expect(result.overallScore).toBeGreaterThan(80);
    expect(result.rating).toBe('Excellent');
  });

  it('should identify potential challenges', async () => {
    const result = await calculateCompatibility(
      { lifePath: 1, expression: 8, soulUrge: 5 },
      { lifePath: 4, expression: 2, soulUrge: 6 }
    );

    expect(result.challenges.length).toBeGreaterThan(0);
  });
});

Production Checklist

Before launching numerology features:

  • Store numerology calculations in your database to avoid repeated API calls
  • Implement Redis caching for frequently accessed compatibility scores
  • Set up rate limiting to prevent API quota exhaustion
  • Add error handling for missing birth dates or invalid data
  • Create admin dashboard to monitor API usage and costs
  • A/B test showing compatibility scores vs hiding them
  • Collect user feedback on accuracy and usefulness
  • Implement analytics to track engagement with numerology features

Next Steps

Once you have basic compatibility working, consider these enhancements:

  • Karmic Lessons: Show shared growth areas for couples (Karmic Debt API endpoint)
  • Monthly Forecasts: Send relationship insights based on Personal Year cycles
  • Compatibility Badges: Display "Excellent Match" badges on profiles
  • Conversation Starters: Generate ice-breakers based on numerology insights
  • Group Compatibility: Calculate dynamics for polyamorous relationships or friend groups

Explore other RoxyAPI products for complementary features:

Conclusion

Integrating numerology into your dating app provides users with meaningful, personalized insights that go beyond superficial matching. The RoxyAPI Numerology API makes implementation straightforward with comprehensive endpoints for compatibility analysis, personality profiles, and relationship forecasts.

Start with basic compatibility scoring, gather user feedback, and expand features based on engagement metrics. Users love personalized insights, and numerology compatibility adds a unique selling point that differentiates your platform.

Ready to get started? Sign up for RoxyAPI and add numerology matching to your dating app today. Full API documentation is available at roxyapi.com/docs.