๐ŸŒ Astrocartography API and โœ‹ Palmistry API are now live. Ship them in your app today.Get Started
Blog/Use cases

Kundli matching APIs for matrimony platforms: what to build and what to skip

The scoring math is a solved problem. The real choices sit around it: which system you follow, how you show doshas, and what you do when a profile has no birth time.

June 19, 2026ยท10 min readยทAstrologyAPI Team
In brief
  • Ashtakoot scores two birth charts out of 36, across eight factors called koots. The API returns the breakdown for each koot. It does not return a verdict.
  • Bhakut and nadi carry 15 of the 36 points. These two koots shape every score you show.
  • The manglik endpoint returns strength, cancellation, and plain reasons. A yes-or-no chip drops most of that. It can even reject couples the API itself recommends.
  • The same two birth records always give the same score. Cache the result, and score at profile view instead of at search time.
  • Ashtakoot is the North Indian system. Do not label it as porutham for South Indian users.

A kundli is a birth chart. Kundli matching compares two charts and scores the match. The scoring part is a solved problem. Your code sends two birth records to our API. A score out of 36 comes back. The math does not change from vendor to vendor. You can wire it up in one afternoon.

The harder choices are about your product:

  • Which system your users expect
  • How your app shows a dosha (a chart condition some families treat as a warning)
  • What you do when a profile has no reliable birth time
  • When and where your code computes the score

These four choices decide whether users trust the feature. Get them wrong and you lose matches without ever learning why.

What users expect on a match screen

In North India, kundli matching usually means Ashtakoot, also called guna milan. It scores a couple out of 36 points. The score is built from eight factors called koots.

Users read this score the way they read a credit score. They want the number. They want to know what counts as high. And they want the breakdown behind the total.

This article stays inside that system. Ashtakoot is what our matching endpoints implement. It is the only matching system we document field by field. (An endpoint is one URL in the API that does one job.) We want to be clear about that up front.

South India follows a different system. Tamil, Telugu and Kannada families often match on porutham. Porutham is a separate set of checks, with its own names and its own count.

A person who grew up with porutham will not read an Ashtakoot score as their tradition. Do not label one as the other. Users notice. They take screenshots. They argue about it in your app store reviews.

Our Match Making package also lists a match_dashakoot_points endpoint. Dashakoot uses ten koots instead of eight. We have not published a field-level guide for it. So we will not describe its response here.

If your product promises a specific regional system, check before you commit. Confirm the endpoint and its fields in the Indian astrology API reference before that promise reaches a spec.

The eight koots and their weights

For each koot, the response returns one object. Each object has total_points, the most that koot can give. It also has received_points, what this pair scored. The eight maximums add up to 36.

KootPointsWhat the response calls it
varna1Natural Refinement / Work
vashya2Innate Giving / Attraction towards each other
tara3Comfort - Prosperity - Health
yoni4Intimate Physical
maitri5Friendship
gan6Temperament
bhakut7Constructive Ability / Society and Couple
nadi8Progeny / Excess

These weights are far from even. Bhakut and nadi carry 15 points between them. That is a little over 40 percent of the total. A couple can take full marks on the first six koots and lose only those two. They still land on 21 out of 36.

The sample response in our matching guide also lands on 21, by a different route. That pair loses tara, yoni and nadi outright. The API also returns a written conclusion. That conclusion calls 21 a reasonably good score. If your screen shows 21 as a failure, it disagrees with the data it just received.

36
Total points
18
Conventional minimum
15
In bhakut + nadi

Alongside the koots, the total object returns minimum_required: 18. That 18 is a convention from astrology. The API does not enforce it. The endpoint computes points and hands them over. Your code decides what counts as a match. Your code also decides whether to show a cutoff at all.

One detail to fix before launch. male_koot_attribute and female_koot_attribute are filled on some koots and empty strings on others. In the sample response, varna returns Kshatriya for both people. Maitri returns Mars for both. Gan and nadi return empty strings. Check these fields before you render them, or you will ship blank rows.

How to show dosha results

This is where matrimony products most often go wrong. The math is fine. The presentation is the problem.

Manglik is the dosha most users ask about. It comes from the position of Mars in a chart. The manglik endpoint checks one chart and returns much more than a yes or a no:

  • is_present, plus a manglik_status string such as EFFECTIVE
  • percentage_manglik_present, and a second percentage after cancellation
  • an is_mars_manglik_cancelled flag
  • a written manglik_report
  • a manglik_present_rule object with plain sentences that name the houses and aspects behind the finding

Most apps throw nearly all of that away. They show one red chip that says Manglik. We think that is the wrong call. The response carries a strength percentage and a cancellation flag. A yes-or-no chip drops both. The data is more careful than the screens built on top of it.

The report endpoint's own sample shows why this matters. match_making_report returns manglik status true for both people, at 27.5 and 28.25 percent. Its conclusion still calls the marriage highly recommended. A hard filter on manglik status would have hidden this couple from each other. Neither would ever have seen the other's profile.

A yes-or-no filter on Manglik status can reject a couple that the API's own report calls highly recommended.

Don't auto-reject. Dosha status should never remove profiles from results on its own. If you offer a dosha filter, make it a choice the user turns on. Show how many profiles it will hide before they apply it. A filter the user chose is a preference. A filter you applied for them is a judgment about their marriage.

Show strength and cancellation beside presence. These three fields belong in one view. A 27.5 percent reading with cancellation applied is one situation. A strong reading with no cancellation is another. The response tells them apart, so your screen should too.

Give the reasons. The based_on_aspect and based_on_house arrays already hold plain sentences. Show them. They turn a verdict into an explanation. A family can take an explanation to their own astrologer and talk it through. For most users, that talk is what they want from this screen.

Families and regions differ more than product teams assume. Some communities treat Manglik status as final. Many treat it as one input among several. And some users check it only because a platform put it in front of them.

If your default settings follow one family's strict view, you make a religious call for every user you have. Keep the default informative. Let each user choose how strict to be.

One wiring note. The manglik endpoint scores one chart at a time. So a couple costs two calls, plus a comparison you write yourself. The report endpoint returns both figures together, as manglik.male_percentage and manglik.female_percentage.

That shape fits a screen that compares two people. Two readings of similar strength lead to one kind of family talk. One person carrying a dosha alone leads to another. A side-by-side view lets the user see which case they are in.

toManglikView.jsjavascript
// The manglik endpoint scores one chart at a time and takes the single-person
// fields: day, month, year, hour, min, lat, lon, tzone. Call it once per
// profile and compare the two results in your own code.
export function toManglikView(manglik) {
  if (!manglik.is_present) {
    return { show: false }
  }

  return {
    show: true,
    // A percentage, not a verdict. Render it.
    strength: manglik.percentage_manglik_present,
    afterCancellation: manglik.percentage_manglik_after_cancellation,
    cancelled: manglik.is_mars_manglik_cancelled,
    status: manglik.manglik_status, // e.g. 'EFFECTIVE'
    // Already plain sentences. Show them instead of a coloured chip.
    reasons: [
      ...manglik.manglik_present_rule.based_on_aspect,
      ...manglik.manglik_present_rule.based_on_house,
    ],
    summary: manglik.manglik_report,
  }
}

This advice has two limits. First, rajju_dosha and vedha_dosha come back from the report endpoint as bare status booleans. They carry no strength figure. There is no extra detail to show. So label them as flags for a person to read, and do not present them as scores.

Second, showing a dosha at all tells users it matters. If your users are not asking for it, you can leave the feature out. That is a fair product choice.

Calling the endpoint

Use match_ashtakoot_points on the base URL https://json.astrologyapi.com/v1. It is a POST call with HTTP Basic auth. Your user ID is the username. Your API key is the password. The body carries sixteen fields, eight per person, with m_ and f_ prefixes. The API needs all sixteen.

Server side only
The host sends no CORS headers, the headers a browser needs for direct calls. So calls straight from a browser fail. Call the API from your server. Keep the API key in a server-only environment variable. If you need a browser-safe path, use a wallet-backed access token instead of the key pair.
scoreMatch.jsjavascript
// scoreMatch.js โ€” Node 18+. Server side only: the API sends no CORS headers.
const AUTH =
  'Basic ' +
  Buffer.from(
    `${process.env.ASTROLOGY_USER_ID}:${process.env.ASTROLOGY_API_KEY}`,
  ).toString('base64')

// Sixteen fields, eight per person: m_ for the male profile, f_ for the
// female one. All sixteen are required.
function toRequestBody(male, female) {
  const sideFor = (person, prefix) => ({
    [`${prefix}_day`]: person.day,
    [`${prefix}_month`]: person.month,
    [`${prefix}_year`]: person.year,
    [`${prefix}_hour`]: person.hour,
    [`${prefix}_min`]: person.min,
    [`${prefix}_lat`]: person.lat,
    [`${prefix}_lon`]: person.lon,
    [`${prefix}_tzone`]: person.tzone,
  })

  return { ...sideFor(male, 'm'), ...sideFor(female, 'f') }
}

export async function scoreMatch(male, female) {
  const response = await fetch(
    'https://json.astrologyapi.com/v1/match_ashtakoot_points',
    {
      method: 'POST',
      headers: {
        Authorization: AUTH,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(toRequestBody(male, female)),
    },
  )

  if (!response.ok) {
    const detail = await response.text()
    throw new Error(`match_ashtakoot_points ${response.status}: ${detail}`)
  }

  return response.json()
}

The response holds the per-koot breakdown, the total object, and a conclusion. The conclusion carries a status flag and a written report. You can show that report as it is. Most teams still write their own wording around the numbers.

Next you map the response into screen data. This step decides how honest the screen will be. The version below keeps every koot and every point lost. It refuses to compute a verdict for the user.

toKootRows.jsjavascript
const KOOTS = [
  'varna',
  'vashya',
  'tara',
  'yoni',
  'maitri',
  'gan',
  'bhakut',
  'nadi',
]

// Keep every koot, keep the losses, decide nothing.
export function toKootRows(score) {
  return KOOTS.map((key) => {
    const koot = score[key]

    return {
      key,
      label: koot.description,
      received: koot.received_points,
      max: koot.total_points,
      // male_koot_attribute and female_koot_attribute come back as empty
      // strings on some koots, so treat the pair as optional.
      attributes:
        koot.male_koot_attribute && koot.female_koot_attribute
          ? [koot.male_koot_attribute, koot.female_koot_attribute]
          : null,
    }
  })
}

const rows = toKootRows(score)

// Heaviest losses first: this is the part users want explained.
const shortfalls = rows
  .filter((row) => row.received < row.max)
  .sort((a, b) => b.max - a.max)

console.log(score.total.received_points, 'of', score.total.total_points)

Want the doshas in the same call? match_making_report takes the same sixteen fields. It returns the ashtakoota total plus manglik, rajju and vedha status. Treat it as a handy bundle, and keep the raw breakdown for the detail view. The full server walkthrough, with error handling, is in the Kundli matching guide.

Start building with real ephemeris data
150 free credits. No card required.

When the birth time is missing

Matrimony signup forms collect birth times that users do not really know. On an Indian platform this happens a lot. And koot scoring suffers more from it than a sun-sign feature would.

The sample response shows why. Bhakut attributes come back as rashi names (a rashi is a zodiac sign). Maitri attributes come back as rashi lords. The written conclusion discusses whether those lords are friendly with each other. The moon's position drives a large share of the score.

On average, the moon stays in one sign for about 2.25 days. A guessed birth time near a boundary can change the sign. A changed sign changes the score. And that score is the number users forward to their families.

There is no rectification endpoint. (Rectification means working out a lost birth time from life events.) Nothing can recover an unknown birth time from other profile data. A form that hints otherwise is a dishonest form. The patterns that work are laid out in the unknown birth time guide:

  • Default to noon, and put the disclaimer on the output itself, never only in a footer
  • Lock time-based features, and show them as locked, until a birth time arrives
  • Ask again later during profile completion instead of blocking signup

For matrimony we go stricter than that general advice. A score built on a guessed time and shown with no disclaimer is worse than no score. This number leaves your product. Users forward it. They quote it to relatives. Some take it to an astrologer, who will ask which birth time produced it. Label the score, or hold it back.

Where to compute the score

Matching is a pure function. That means the same two birth records always produce the same score. This makes caching easy. Cache on the ordered pair of cleaned-up birth records. Store the result. Clear it only when a user edits their own birth data.

The same trait settles where the call should happen. Score at search time, and a page of fifty results fires fifty network calls before anything renders. Most of those scores are never read.

Score at profile view instead, or behind a button the user taps to check the match. That is one call per pair someone actually cared about. You make it once for the life of that pair.

We do not publish per-endpoint latency figures (how long each call takes). So plan for a third-party call that can take an unknown amount of time. Do not build a flow that waits on it. Render the profile first. Load the score into it after. Never let a matching call block your search results.

Precomputing every pair is the other tempting mistake. That table grows with the square of your user count. Almost none of those scores would ever be seen. Compute a score only when someone asks for it, then keep it.

What a match costs

The matching endpoints live in the Vedic suite. It starts at $29 a month, or โ‚น1,500 in India. Pay-as-you-go is the other route. You top up a wallet, sign calls with an access token, and pay per call with no plan attached. That route suits a platform still testing whether users want matching at all. Both routes are laid out on the pricing page.

Credit cost varies by endpoint. The current figures live in the API Pricing Catalog. We keep them out of articles, because articles go stale. The number that drives your spend is calls per match. You can work that number out in advance.

Score at search time, and calls per match multiply by results per page, then again by every repeat search. Score at profile view with a cache, and it drops to one call per distinct pair, forever.

New accounts start with 150 free credits. That is enough to run the matching endpoints on real birth data. You can read the real response shapes before you commit to anything.

The math itself is the same for every platform. Anyone in this market can buy the same 36 points. What users will remember is the screen around those points. They will remember whether it treated their marriage as their own decision, or as a filter.

Frequently asked questions

What is a good Ashtakoot score?

The response returns a minimum_required value of 18 out of 36. That 18 is a convention from astrology. The API does not enforce it. The sample response in our matching guide scores 21. Its own written conclusion calls that a reasonably good score. Your product decides the cutoff, or whether to show one at all.

Can kundli matching work without a birth time?

Not reliably. Koot scoring leans on the moon position. The moon stays in one sign for about 2.25 days. A guessed time near a boundary can change the score. There is no endpoint that can recover a lost birth time. Default to noon with a clear disclaimer, or hold the score until the user gives a time.

Is there an API for South Indian porutham matching?

Not at field level. The matching endpoints we document use the North Indian Ashtakoot system. A match_dashakoot_points endpoint exists in the Match Making package, but it has no field-level guide yet. Check the exact fields in the Vedic API reference before you promise a specific regional system.

How fast is kundli matching via API?

We do not publish per-endpoint latency figures. Treat matching as a third-party network call that can take an unknown amount of time. Score at profile view instead of during search, and cache the result: the same two birth records always produce the same score.

Further reading
Start building with real ephemeris data
150 free credits. No card required.
Related