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

17 min read
Brett Calloway
numerologyDating AppsMatchmakingAPI IntegrationCompatibility

Add numerology-powered matchmaking to a dating app with three endpoints: chart, compatibility, and personal year. Working code, caching, multi-language, MCP shortcut.

TL;DR

  • Add numerology-powered matchmaking to a dating app in three endpoints: POST /api/v2/numerology/chart, POST /api/v2/numerology/compatibility, and POST /api/v2/numerology/personal-year.
  • The Compatibility endpoint accepts pre-calculated Life Path, Expression, and Soul Urge numbers, or raw name and birthdate. Returns a 0 to 100 score, per-aspect breakdown, strengths, challenges, and advice.
  • One API key covers 12 spiritual domains (numerology, tarot, Western astrology, Vedic, biorhythm, I-Ching, crystals, dreams, angel numbers, location). Multi-language responses in 8 languages via ?lang=.
  • Skip the SDK boilerplate: install our Remote MCP and let your AI coding assistant generate the integration. claude mcp add --transport http roxy-numerology https://roxyapi.com/mcp/numerology.

About the author: Brett Calloway is a Developer Advocate and AI Integration Specialist with 12 years of experience building APIs and developer tooling, including three years focused on AI-native infrastructure for spiritual and wellness applications. He writes on building context-rich AI agents using Model Context Protocol, drawing on a developer relations background.

Dating apps are constantly looking for new ways to help users find meaningful connections. Swiping and behavioral algorithms work, but numerology compatibility adds a personal dimension that sets a platform apart. This guide shows you how to integrate numerology-based matchmaking using the Numerology API: three endpoints, working React Native code, caching strategy, and the MCP shortcut for AI-assisted integration.

Why add numerology compatibility to a dating app?

Numerology compatibility analysis gives users personality-based matching that goes beyond photos and bios. Each user gets a Life Path, Expression, and Soul Urge number derived from their birth date and full birth name. Two users get a weighted compatibility score (Life Path 50%, Expression 30%, Soul Urge 20%) on a 0 to 100 scale, with named strengths, challenges, and practical advice. The data requirements are minimal: just date of birth and full name, both of which most dating apps already collect.

Personalized cosmic apps have proven users love this kind of insight. Numerology is faster to compute than astrology (no ephemeris lookup, no timezone math) and the response shape is simple: numbers and prose, no chart rendering required. The five concrete benefits:

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

What you will build

Three core features for a dating app, all backed by the Numerology API:

  1. Compatibility scoring: match percentages between two users
  2. Personality profiles: full numerology profiles on user pages
  3. Daily insights: Personal Year and Personal Month forecasts

Ready to build this? Numerology API gives you 16 endpoints under one key, including chart, compatibility, life path, soul urge, and personal cycles. See pricing.

Prerequisites

  • Node.js 18+ or Bun runtime
  • RoxyAPI account with API key, sign up at pricing
  • Existing dating app with user profiles (date of birth required)
  • React Native or Next.js project (the examples work for both)

How do I integrate numerology with a dating app stack?

The simplest path is three POST calls against the Numerology API. Each call accepts a JSON body and returns a typed response with full interpretations. All calculations are deterministic, so the responses are perfectly cacheable. The Numerology API is one of 12 domains under a single subscription, and every endpoint is verified against authoritative Pythagorean numerology standards (see methodology).

Set up the API client first:

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

async function post<T>(path: string, body: unknown): Promise<T> {
  const response = await fetch(`${NUMEROLOGY_API}${path}`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify(body),
  });
  if (!response.ok) {
    throw new Error(`Numerology API error ${response.status}: ${response.statusText}`);
  }
  return response.json() as Promise<T>;
}

export const calculateCompatibility = (
  person1: { lifePath: number; expression: number; soulUrge: number },
  person2: { lifePath: number; expression: number; soulUrge: number }
) => post('/compatibility', { person1, person2 });

export const generateChart = (fullName: string, year: number, month: number, day: number) =>
  post('/chart', { fullName, year, month, day });

export const calculateLifePath = (year: number, month: number, day: number) =>
  post('/life-path', { year, month, day });

export const calculatePersonalYear = (month: number, day: number, year?: number) =>
  post('/personal-year', { month, day, year });

Store your API key in environment variables:

# .env.local
ROXY_API_KEY=your_api_key_here

Skip the integration code with Remote MCP

If you use Claude Code, Cursor, or VS Code Copilot, install our Remote MCP server and let the assistant generate the matching client and React components for you:

claude mcp add --transport http roxy-numerology https://roxyapi.com/mcp/numerology

Then prompt the assistant: "Generate a React Native compatibility card using the calculateNumCompatibility tool." The MCP server exposes every numerology endpoint as a typed tool with the same Zod schemas the API enforces, so the assistant can fact-check field names without guessing. See the Claude Code guide for setup, or the MCP overview for other clients.

How does compatibility scoring work?

The compatibility endpoint accepts two persons and returns a weighted match score on a 0 to 100 scale. Each person can be passed as pre-calculated numbers (Life Path, Expression, Soul Urge) or as raw inputs (full name, year, month, day) for automatic calculation. You can mix modes: numbers for one person, raw inputs for the other. Life Path carries 50% of the weighting, Expression 30%, Soul Urge 20%. The response includes per-aspect compatibility, named strengths and challenges, and practical advice.

Calculate and store core numbers on profile creation

When a user completes their profile, calculate their core numbers once and store them. The numbers never change for that birth date and name combination, so this is a one-time call per user.

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

export async function calculateUserNumerology(
  userId: string,
  fullName: string,
  year: number,
  month: number,
  day: number
) {
  const chart = await generateChart(fullName, year, month, day);

  await prisma.userNumerology.upsert({
    where: { userId },
    create: {
      userId,
      lifePath: chart.coreNumbers.lifePath.number,
      expression: chart.coreNumbers.expression.number,
      soulUrge: chart.coreNumbers.soulUrge.number,
      personality: chart.coreNumbers.personality.number,
      fullChart: chart,
    },
    update: {
      lifePath: chart.coreNumbers.lifePath.number,
      expression: chart.coreNumbers.expression.number,
      soulUrge: chart.coreNumbers.soulUrge.number,
      personality: chart.coreNumbers.personality.number,
      fullChart: chart,
    },
  });

  return chart;
}

Calculate compatibility on match view

When the user opens a potential match, fetch the stored numbers for both and call the compatibility endpoint:

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

export async function getMatchCompatibility(userId: string, targetUserId: string) {
  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');
  }

  return calculateCompatibility(
    { lifePath: user.lifePath, expression: user.expression, soulUrge: user.soulUrge },
    { lifePath: target.lifePath, expression: target.expression, soulUrge: target.soulUrge }
  );
}

Compatibility response shape

{
  "overallScore": 82,
  "rating": "Very Compatible",
  "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."
  },
  "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."
  },
  "soulUrge": {
    "person1": 6,
    "person2": 2,
    "compatibility": 90,
    "description": "6 and 2 share exceptional emotional compatibility. Both prioritize harmony, nurturing, and partnership."
  },
  "strengths": [
    "Both value freedom and new experiences",
    "Excellent social chemistry and communication",
    "Shared appreciation for creativity and adventure"
  ],
  "challenges": [
    "Number 5 may feel restrained by the attention needs of 3",
    "Number 7 needs solitude that can conflict with social energy"
  ],
  "advice": "Embrace your shared love for adventure while respecting each persons unique needs. Schedule regular quality time and individual space for personal growth."
}

The rating field is one of: Highly Compatible, Very Compatible, Compatible, Moderately Compatible, Challenging. Verify the live response shape via POST /numerology/compatibility.

Displaying compatibility in the UI

A match card component built on React Native primitives. Adapt the same logic to React or Next.js by swapping styled views for divs and the StyleSheet object for CSS classes.

// 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 }),
        });
        setCompatibility(await response.json());
      } 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';
    if (score >= 60) return '#F59E0B';
    return '#EF4444';
  };

  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="LP" />
        <AspectScore label="Expression" score={compatibility.expression.compatibility} icon="EX" />
        <AspectScore label="Soul Urge" score={compatibility.soulUrge.compatibility} icon="SU" />
      </View>

      <View style={styles.strengthsContainer}>
        <Text style={styles.sectionTitle}>Strengths</Text>
        {compatibility.strengths.slice(0, 3).map((s: string, i: number) => (
          <Text key={i} style={styles.bulletPoint}>{`- ${s}`}</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 },
  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: 18, fontWeight: '700', 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 },
});

How do I show full numerology profiles on user pages?

Call POST /numerology/chart once per user and store the response. The chart endpoint returns six core numbers (Life Path, Expression, Soul Urge, Personality, Birth Day, Maturity), karmic lessons, karmic debt, the current Personal Year and Personal Month, four Pinnacles and Challenges with age ranges, Hidden Passion, Subconscious Self, name letter analysis, lucky associations, and a holistic prose summary. One call covers the entire profile page.

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

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

  useEffect(() => {
    fetch(`/api/users/${userId}/numerology`)
      .then((r) => r.json())
      .then(setChart)
      .catch((e) => console.error('Failed to fetch chart:', e));
  }, [userId]);

  if (!chart) return null;

  return (
    <ScrollView style={styles.container}>
      <NumberCard title="Life Path" data={chart.coreNumbers.lifePath} />
      <NumberCard title="Expression" data={chart.coreNumbers.expression} />
      <NumberCard title="Soul Urge" data={chart.coreNumbers.soulUrge} />

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

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

function NumberCard({ title, data }: { title: string; data: any }) {
  return (
    <View style={styles.card}>
      <Text style={styles.cardTitle}>{title}</Text>
      <Text style={styles.cardNumber}>Number {data.number}</Text>
      <View style={styles.keywordsContainer}>
        {data.meaning.keywords.slice(0, 4).map((kw: string, i: number) => (
          <View key={i} style={styles.keywordBadge}>
            <Text style={styles.keywordText}>{kw}</Text>
          </View>
        ))}
      </View>
      <Text style={styles.cardDescription}>{data.meaning.description}</Text>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#F9FAFB' },
  card: { backgroundColor: '#FFFFFF', borderRadius: 16, padding: 20, marginHorizontal: 15, marginVertical: 10 },
  cardTitle: { fontSize: 18, fontWeight: '600', color: '#111827' },
  cardNumber: { fontSize: 14, color: '#6B7280', marginTop: 2, marginBottom: 12 },
  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 },
});

How should I cache numerology calculations at scale?

Numerology calculations are deterministic: the same inputs always produce the same output. Two layers of caching cover most production traffic. First, the API itself sets X-Cache-TTL: 86400 on every response, so any caching CDN or shared HTTP cache in front of your backend reuses results for 24 hours without you writing code. Second, store the per-user chart in your database on profile creation and the per-pair compatibility result in Redis with a long TTL. Compatibility scores never change for a fixed pair of birth dates and names, so cache aggressively.

// 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) {
  const key = `compat:${[user1Id, user2Id].sort().join(':')}`;
  return redis.get(key);
}

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

Wire it into your API route:

// 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;

  let compatibility = await getCachedCompatibility(userId, targetUserId);
  if (!compatibility) {
    compatibility = await getMatchCompatibility(userId, targetUserId);
    await setCachedCompatibility(userId, targetUserId, compatibility);
  }

  res.json(compatibility);
}

Daily insights with Personal Year cycles

The POST /numerology/personal-year endpoint returns the current 1 to 9 cycle for a user, with theme, opportunities, challenges, and advice. Pair it with a small switch table for one-line daily messages:

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

const DAILY_MESSAGES: Record<number, string> = {
  1: 'Today is perfect for starting new projects and taking initiative in relationships.',
  2: 'Focus on cooperation and listening today. Patience brings rewards.',
  3: 'Express yourself creatively today. Communication is at its peak.',
  4: 'Build stability today. Focus on trust and consistency.',
  5: 'Embrace spontaneity today. Try something new with your matches.',
  6: 'A day to deepen emotional connections and show 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 no longer serves.',
};

export async function getDailyInsight(userId: string) {
  const user = await prisma.user.findUnique({ where: { id: userId } });
  if (!user?.birthDate) return null;

  const birthDate = new Date(user.birthDate);
  const personalYear: any = await calculatePersonalYear(
    birthDate.getMonth() + 1,
    birthDate.getDate()
  );

  return {
    personalYear: personalYear.personalYear,
    insight: DAILY_MESSAGES[personalYear.personalYear],
    theme: personalYear.theme,
    forecast: personalYear.forecast,
  };
}

Going global: multi-language responses

The Numerology API serves responses in 8 languages via a ?lang= query parameter: English (default), German, Spanish, French, Hindi, Portuguese, Russian, Turkish. All meanings, descriptions, advice, and forecasts are translated, not just the labels. Pass the user locale to every endpoint:

const response = await fetch(`${NUMEROLOGY_API}/chart?lang=${userLocale}`, { ... });
Localelang= value
Englishen (or omit)
Germande
Spanishes
Frenchfr
Hindihi
Portuguesept
Russianru
Turkishtr

For dating apps shipping in non-English markets this saves you maintaining a translation pipeline for personality content.

Beyond compatibility: cross-domain features

Numerology is one of 12 spiritual domains under the same API key. The most useful neighbors for a dating app:

You ship one auth flow and one API key for all of them.

Monetization

Numerology features fit cleanly into common dating-app pricing patterns:

PatternHow it works
FreemiumFree overall score, premium unlocks per-aspect breakdown and full chart
CreditsUsers buy credits; each detailed compatibility report costs 1 credit
Subscription tierBundle numerology + chart in your premium tier
BoostHighlight matches with score above 80 in the standard feed

Frequently asked questions

Q: Is the Numerology API free to use? A: There is a free tier for evaluation. Production usage starts at the Starter plan. All 12 domains and every endpoint are included in every plan, billed by request volume. See pricing for current quotas.

Q: How accurate is the compatibility score? A: The endpoint uses standard Pythagorean numerology with a 50/30/20 weighting (Life Path, Expression, Soul Urge) and a per-number compatibility matrix verified against authoritative numerology references. See methodology for how we test.

Q: Do I need to pre-calculate Life Path, Expression, and Soul Urge? A: No. The compatibility endpoint accepts either pre-calculated numbers or raw inputs (full name plus year, month, day) and runs the calculation server-side. You can also mix modes per person.

Q: What happens to compatibility scoring when one user has only a date of birth? A: Pass just the Life Path number; the endpoint will weight Life Path at 100% and skip Expression and Soul Urge in the breakdown. Useful for users who skipped the full-name field at signup.

Q: Can I use the Numerology API with Claude Code, Cursor, or other AI coding assistants? A: Yes. Add the Remote MCP server with claude mcp add --transport http roxy-numerology https://roxyapi.com/mcp/numerology and the assistant gets typed access to every endpoint. See the Claude Code guide, Cursor guide, or MCP overview for setup.

Conclusion

Numerology compatibility adds a personalized dimension to dating apps that purely behavioral matching cannot replicate. Three POST endpoints, deterministic responses, and one-time calculations per user mean the integration cost is low and the cache hit rate is near 100%. Start with the overall score on the match card, then layer in the full chart on profile pages and Personal Year insights for daily engagement.

Ready to ship? Numerology API covers the full integration with 16 endpoints. See pricing.