- Docs
- What To Build
- Dating Compatibility App
Build a zodiac dating compatibility app
Ship a two-chart compatibility UI that returns a 0 to 100 score with strengths and challenges. Time to ship: 30 minutes.
Dating and matchmaking apps are the highest-demand pattern in the astrology API market. This tutorial wires the compatibility-score feature that sits on a match card. The underlying call is POST /astrology/compatibility-score. For the full synastry report with inter-aspects, the code pattern is identical, just swap the URL.
What you can build with this
- Match-card compatibility badges (0 to 100 score)
- Pro-tier synastry readings with inter-aspect breakdowns
- Matrimonial apps using Vedic Gun Milan (36-point Ashtakoota)
- Numerology compatibility side-features ("Life Path 5 meets Life Path 7")
- AI matchmaker chatbots that read two users and explain the fit
What you need, 30 seconds
- A Roxy API key. Get one on the pricing page.
- Two birth dates, times, and cities. We geocode the cities in step 2.
Step 1, call your first endpoint
The compatibility score is one POST with two people. Pick a language and paste.
curl -X POST https://roxyapi.com/api/v2/astrology/compatibility-score \
-H "X-API-Key: $ROXY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"person1": {"date":"1990-07-15","time":"14:30:00","latitude":40.7128,"longitude":-74.006,"timezone":"America/New_York"},
"person2": {"date":"1992-03-22","time":"09:00:00","latitude":34.0522,"longitude":-118.2437,"timezone":"America/Los_Angeles"}
}'
import { createRoxy } from '@roxyapi/sdk';
const roxy = createRoxy(process.env.ROXY_API_KEY!);
const { data } = await roxy.astrology.calculateCompatibility({
body: {
person1: { date: '1990-07-15', time: '14:30:00', latitude: 40.7128, longitude: -74.006, timezone: 'America/New_York' },
person2: { date: '1992-03-22', time: '09:00:00', latitude: 34.0522, longitude: -118.2437, timezone: 'America/Los_Angeles' },
},
});
console.log(data.overallScore); // 78
console.log(data.relationshipArchetype); // "The Seeker and The Builder"
console.log(data.advice);
import os
from roxy_sdk import create_roxy
roxy = create_roxy(os.environ['ROXY_API_KEY'])
result = roxy.astrology.calculate_compatibility(
person1={'date': '1990-07-15', 'time': '14:30:00', 'latitude': 40.7128, 'longitude': -74.006, 'timezone': 'America/New_York'},
person2={'date': '1992-03-22', 'time': '09:00:00', 'latitude': 34.0522, 'longitude': -118.2437, 'timezone': 'America/Los_Angeles'},
)
print(result['overallScore'])
print(result['relationshipArchetype'])
claude mcp add-json --scope user roxy-astrology '{"type":"http","url":"https://roxyapi.com/mcp/astrology","headers":{"X-API-Key":"YOUR_KEY"}}'
claude mcp add-json --scope user roxy-location '{"type":"http","url":"https://roxyapi.com/mcp/location","headers":{"X-API-Key":"YOUR_KEY"}}'
Then ask Claude, "calculate compatibility between someone born July 15 1990 2:30pm in New York and someone born March 22 1992 9am in Los Angeles." The agent geocodes both cities and calls the score endpoint. Full setup for Cursor, Claude Desktop, Antigravity, and other clients: MCP guide.
Response fields to use: overallScore (0-100), relationshipArchetype, keyAspects[], elementBalance, advice.
Step 2, scaffold the page
Create compatibility.html. The HTML is 50 lines, the script 40.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Zodiac Compatibility</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: system-ui, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; background: #fafafa; }
h1 { text-align: center; margin-bottom: 8px; }
.subtitle { text-align: center; color: #666; margin-bottom: 24px; }
.people { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 24px; }
.person { background: white; padding: 16px; border-radius: 12px; }
.person h3 { margin-bottom: 12px; }
label { display: block; font-size: 13px; font-weight: 500; margin-bottom: 4px; }
input, select { width: 100%; padding: 8px; border: 1px solid #ddd; border-radius: 6px; margin-bottom: 10px; }
button.check { display: block; width: 100%; padding: 14px; background: #7c3aed; color: white; border: none; border-radius: 12px; font-size: 16px; font-weight: 600; cursor: pointer; margin-bottom: 24px; }
.result { display: none; }
.ring { text-align: center; margin-bottom: 20px; }
.ring .n { font-size: 64px; font-weight: 700; color: #7c3aed; }
.ring .l { font-size: 14px; color: #666; }
.cats { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 20px; }
.cat { background: white; padding: 12px; border-radius: 8px; }
.cat .k { font-size: 12px; font-weight: 600; text-transform: uppercase; color: #666; }
.cat .v { font-size: 20px; font-weight: 600; }
.insights { background: white; padding: 16px; border-radius: 12px; }
.insights h3 { margin-bottom: 8px; }
.insights ul { padding-left: 20px; margin-bottom: 12px; }
.summary { padding: 12px; background: #f5f0ff; border-radius: 8px; margin-bottom: 16px; }
@media (max-width: 500px) { .people, .cats { grid-template-columns: 1fr; } }
</style>
</head>
<body>
<h1>Zodiac Compatibility</h1>
<p class="subtitle">Two birth charts in, one score out</p>
<div class="people">
<div class="person">
<h3>Person 1</h3>
<label>Birth date</label><input type="date" id="p1-date" value="1990-07-15" />
<label>Birth time</label><input type="time" id="p1-time" value="14:30" />
<label>City</label>
<select id="p1-city">
<option value="40.7128,-74.006,America/New_York">New York</option>
<option value="34.0522,-118.2437,America/Los_Angeles">Los Angeles</option>
<option value="51.5074,-0.1278,Europe/London">London</option>
<option value="19.076,72.8777,Asia/Kolkata">Mumbai</option>
<option value="35.6762,139.6503,Asia/Tokyo">Tokyo</option>
</select>
</div>
<div class="person">
<h3>Person 2</h3>
<label>Birth date</label><input type="date" id="p2-date" value="1992-03-22" />
<label>Birth time</label><input type="time" id="p2-time" value="09:00" />
<label>City</label>
<select id="p2-city">
<option value="34.0522,-118.2437,America/Los_Angeles">Los Angeles</option>
<option value="40.7128,-74.006,America/New_York">New York</option>
<option value="51.5074,-0.1278,Europe/London">London</option>
<option value="19.076,72.8777,Asia/Kolkata">Mumbai</option>
<option value="35.6762,139.6503,Asia/Tokyo">Tokyo</option>
</select>
</div>
</div>
<button class="check" onclick="run()">Check compatibility</button>
<div id="result" class="result"></div>
<script>
const API_KEY = 'YOUR_API_KEY';
function read(prefix) {
const [lat, lng, tz] = document.getElementById(prefix + '-city').value.split(',');
return {
date: document.getElementById(prefix + '-date').value,
time: document.getElementById(prefix + '-time').value + ':00',
latitude: parseFloat(lat),
longitude: parseFloat(lng),
timezone: tz,
};
}
async function run() {
const el = document.getElementById('result');
el.style.display = 'block';
el.innerHTML = '<div style="text-align:center;padding:40px;">Comparing birth charts...</div>';
const res = await fetch('https://roxyapi.com/api/v2/astrology/compatibility-score', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
body: JSON.stringify({ person1: read('p1'), person2: read('p2') }),
});
const data = await res.json();
const cats = data.elementBalance || {};
const catEntries = Object.entries(cats);
el.innerHTML =
'<div class="ring"><div class="n">' + (data.overallScore ?? '') + '</div><div class="l">out of 100</div></div>' +
(data.advice ? '<div class="summary">' + data.advice + '</div>' : '') +
(catEntries.length
? '<div class="cats">' + catEntries.map(([k, v]) =>
'<div class="cat"><div class="k">' + k + '</div><div class="v">' + v + '</div></div>'
).join('') + '</div>'
: '') +
'<div class="insights">' +
(data.keyAspects && data.keyAspects.length
? '<h3>Key aspects</h3><ul>' + data.keyAspects.map(k =>
'<li>' + (typeof k === 'string' ? k : (k.description || JSON.stringify(k))) + '</li>'
).join('') + '</ul>'
: '') +
(data.relationshipArchetype
? '<h3>Archetype</h3><p>' + data.relationshipArchetype + '</p>'
: '') +
'</div>';
}
</script>
</body>
</html>
Replace YOUR_API_KEY. Save. Double-click to open. Click the button.
Timezone is passed as an IANA string ("America/New_York") not a decimal. The server resolves it to the DST-correct offset for the birth date, so a January 1990 New York birth gets EST, a July birth gets EDT. You no longer need to guess whether to send -4 or -5.
Step 3, upgrade to production
Three upgrades that take this from demo to real.
- Move the key server-side. Put the fetch behind a Next.js route, a Vercel function, or a Cloudflare Worker. The Next.js integration guide is the drop-in recipe.
- Geocode any city. Replace the dropdown with
GET /api/v2/location/search?q={query}. Roxy returns a paginated list withlatitude,longitude, and IANAtimezone. Usecities[0]. - Swap compatibility-score for synastry.
POST /astrology/synastryreturns inter-aspects, strengths, challenges, and acompatibilityScore. Same inputs, richer output. Premium tier.
For matrimonial apps targeting India, swap to POST /vedic-astrology/compatibility for the full 36-point Ashtakoota breakdown with dosha cancellation. See the Vedic astrology guide.
Step 4, deploy
Drop the file into Cloudflare Pages, Vercel, or Netlify. Three-minute deploy. Swap the hardcoded key for an environment variable and a serverless proxy before you share the URL.
Gotchas
- Do not ship the API key in browser code. The HTML above works on localhost. For anything public, move the fetch to a serverless function.
- Time must include seconds.
14:30:00not14:30. Most<input type="time">elements returnHH:MM, so append:00before sending. - Both persons need full birth data.
date,time,latitude,longitude,timezone. Missing time leads to degraded accuracy, not a server error. - IANA beats decimal for DST correctness.
"America/New_York"resolves to EST or EDT automatically.-5is always EST. - Synastry is
/astrology/synastry, not/synastry. The route is nested under/astrology. Easy typo.
What to build next
- The tarot app tutorial adds a second reading type with the same fetch pattern.
- The AI chatbot tutorial lets users type birth data in natural language and have an LLM fill the form for them.
- The Western astrology guide lists every endpoint you can swap into the fetch call.
- The Vedic astrology guide covers Gun Milan for matrimonial builds.