Palmistry API

Read a palm from a photo, in any of 30 language codes. Then ask questions about it, compare two palms, set it beside a birth chart, or hand your customer a PDF.

Send one photo of a hand, or one photo for each hand. You get the lines, mounts, fingers and hand shape that the photo shows, and a written reading. Every reading comes back with a readingId. Use it for follow-up questions, compatibility, a chart cross-check and a PDF report without sending the photo again.

RouteWhat it doesPrice
POST /v2/palmistry/ai-readingReads one photo, or both hands$0.05 per reading
POST /v2/palmistry/askAnswers a question about a stored readingPer 1M tokens, 1 cent minimum
POST /v2/palmistry/compatibilityCompares two stored readings$0.02
POST /v2/palmistry/with-chartSets one reading beside a Vedic birth chart$0.03
POST /v2/palmistry/report-pdfA branded PDF of a stored reading$0.10
POST /v2/palmistry/delete-readingDeletes a stored readingFree

#Quick start

You need a production key (vk_live_ or vk_ent_) and a wallet balance. To try the routes first at no charge, use the sandbox. Send the key as Authorization: Bearer <key>. Every call is a POST with a JSON body.

Step 1. Read a photo. Send it as base64 (a data:image/jpeg;base64,... value is fine). Set your client timeout to at least 90 seconds.

jq -n --rawfile img <(base64 < palm.jpg | tr -d '\n') \
  '{image: ("data:image/jpeg;base64," + $img), hand: "right", language: "en"}' > body.json

curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/ai-reading \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  --data-binary @body.json
import base64, os, uuid, requests

with open("palm.jpg", "rb") as f:
    photo = base64.b64encode(f.read()).decode()

r = requests.post(
    "https://api.vedika.io/v2/palmistry/ai-reading",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"image": "data:image/jpeg;base64," + photo, "hand": "right", "language": "en"},
    timeout=120,  # readings usually take 10-30 s; allow at least 90 s
)
body = r.json()
if r.status_code == 200:
    print(body["data"]["readingId"], body["data"]["handShape"]["name"])
else:
    print(r.status_code, body["code"], body.get("error") or body.get("message"))
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const photo = (await readFile("palm.jpg")).toString("base64");

const r = await fetch("https://api.vedika.io/v2/palmistry/ai-reading", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    image: "data:image/jpeg;base64," + photo,
    hand: "right",
    language: "en",
  }),
  signal: AbortSignal.timeout(120_000), // allow at least 90 s
});
const body = await r.json();
if (r.ok) console.log(body.data.readingId, body.data.handShape.name);
else console.log(r.status, body.code, body.error ?? body.message);

Step 2. Keep data.readingId. It looks like palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF: palm_ and 26 characters. It stays valid for 30 days and works only for your account.

Step 3. Ask a follow-up question with that id.

curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/ask \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
  "question": "What does my heart line say about relationships?",
  "language": "en"
}'
import os, uuid, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/ask",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
        "question": "What does my heart line say about relationships?",
        "language": "en"
    },
    timeout=120,
)
print(r.status_code, r.json())
import { randomUUID } from "node:crypto";

const r = await fetch("https://api.vedika.io/v2/palmistry/ask", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "question": "What does my heart line say about relationships?",
    "language": "en"
  }),
  signal: AbortSignal.timeout(120_000),
});
console.log(r.status, await r.json());

#Read a photo

POST /v2/palmistry/ai-reading. Reads one photo, or one photo per hand. A finished reading costs $0.05, the same for every language and for two hands.

#Request

FieldRequiredMeaning
imageYes, or imagesOne photo as base64 or a data URL. JPEG, PNG, WebP or GIF, up to 5 MiB after decoding. HEIC is refused: send a JPEG or PNG. Image URLs are not accepted.
imagesYes, or imageOne or two entries, each {"image": base64, "hand": "left" or "right"}. Send two entries to read both hands. Two entries need one left and one right. Do not send image and images together.
handNoleft or right: the hand in a single photo. The reading follows the hand you name. If the photo plainly shows the other hand, the reading still follows the hand you named and handNote says so. If you leave it out, the reading uses the hand the photo appears to show (observedHand), and handNote says so.
dominantHandNoleft or right. Default right. Sets which hand the reading treats as dominant.
languageNoLanguage of the written reading. Default en. All 31 language codes in the language table work. lang and langcode are accepted too.
overlayNotrue also traces the main lines on the photo. One photo only. Billed as an extra, see Line overlay.

#Read both hands

Send two photos in images. You get one readingId for the pair, a reading for each hand under data.hands.left and data.hands.right, and data.comparison, which sets the dominant hand against the other one. The price is the same $0.05.

curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/ai-reading \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "images": [
    {
      "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg…",
      "hand": "left"
    },
    {
      "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg…",
      "hand": "right"
    }
  ],
  "dominantHand": "right",
  "language": "en"
}'
import os, uuid, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/ai-reading",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "images": [
            {
                "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg…",
                "hand": "left"
            },
            {
                "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg…",
                "hand": "right"
            }
        ],
        "dominantHand": "right",
        "language": "en"
    },
    timeout=120,
)
print(r.status_code, r.json())
import { randomUUID } from "node:crypto";

const r = await fetch("https://api.vedika.io/v2/palmistry/ai-reading", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    "images": [
      {
        "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg…",
        "hand": "left"
      },
      {
        "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg…",
        "hand": "right"
      }
    ],
    "dominantHand": "right",
    "language": "en"
  }),
  signal: AbortSignal.timeout(120_000),
});
console.log(r.status, await r.json());

#Same photo, same reading

Send the same photo again from the same account, with the same hand, dominantHand, language and overlay, and you get the same reading back for 30 days, whatever the Idempotency-Key. The reading is written once and kept, so a repeat does not give a different hand shape or element. The same photo also names the same hand shape and element in every language. Two identical requests sent at the same moment also get one reading.

This is not a refund. A new request (a new Idempotency-Key) is a new purchase at the same price, $0.05, even when it returns a reading you already have. To be charged once for a retry, use the same key: see Idempotency and retries. A photo that was refused is not kept: it is read again next time, so a photo once judged unclear can still be read.

#Line overlay

Set overlay: true with one photo. The response then carries data.linePaths (the traced path of each of the six lines, with a confidence from 0 to 1, so your app can draw its own), data.overlay (the photo with the lines drawn on it, as base64 JPEG) and data.overlayUsage (the extra tokens and their price). The overlay is an approximate guide (overlay.approximate is always true). A traced line is drawn when its confidence is at least 0.2 and its path passes basic shape checks (not along the photo edge, heart above head, head and life meeting only near the thumb side, a life line that curves the way the reading says); lines below 0.6 are dashed. A traced line that is not drawn stays in linePaths with drawn: false and a reason, and is listed in overlay.notDrawn. The overlay's extra tokens are priced per 1M tokens at Vedika Pro Ultra rates: $0.40 per 1M input tokens and $2.00 per 1M output tokens. That price is added to the $0.05 and the total is settled in whole cents. An overlay adds a few hundred tokens, a fraction of a cent, so it usually adds nothing to the bill. overlayUsage.usd shows the exact price of the extra tokens; billing.charged shows what was charged.

#Response

{
  "success": true,
  "data": {
    "system": "palmistry",
    "type": "ai-reading",
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "language": "en",
    "hand": "right",
    "handDetected": "right",
    "requestedHand": "right",
    "handNote": null,
    "handRole": {
      "dominantHand": "right",
      "meaning": "The dominant hand shows how you are shaping your life now: your choices, habits and outer story.",
      "role": "dominant"
    },
    "imageQuality": "usable",
    "handShape": {
      "characteristics": [
        "Square palm"
      ],
      "element": "Earth",
      "name": "Earth Hand",
      "personality": "Practical, grounded, reliable, traditional values, love of nature"
    },
    "lines": [
      {
        "aspects": [],
        "interpretation": "Present with typical depth and length; it relates to vitality, life energy, major life changes, physical health trajecto…",
        "key": "life",
        "location": "Curves from between thumb and index finger toward wrist",
        "meaning": "Vitality, life energy, major life changes, physical health trajectory",
        "name": "Life Line",
        "observed": {
          "breaks": 0,
          "curve": "strong",
          "depth": "clear",
          "end": "wrist",
          "forks": "none",
          "islands": 0,
          "length": "medium",
          "present": true,
          "start": "thumb_side"
        },
        "confidence": 0.9
      }
    ],
    "mounts": [
      {
        "aspect": "balanced",
        "interpretation": "Balanced development: neither emphasised nor diminished.",
        "key": "jupiter",
        "location": "Below index finger",
        "meaning": "Ambition, leadership, confidence, spirituality",
        "name": "Mount of Jupiter",
        "observed": {
          "prominence": "normal"
        },
        "planet": "Jupiter"
      }
    ],
    "fingers": [
      {
        "interpretation": "Balanced ambition, fair-minded.",
        "key": "index",
        "meaning": "Ambition, ego, leadership, self-image",
        "name": "Index Finger (Jupiter)",
        "observed": {
          "length": "average",
          "shape": "straight"
        },
        "planet": "Jupiter"
      }
    ],
    "reading": {
      "careerAndMoney": "The fate line is short and faint, suggesting that your direction may be shaped by your own choices. The Jupiter and Satu…",
      "cautions": [
        "faint heart line may mean reserved feelings"
      ],
      "fingersAndThumb": "The fingers are mostly average in length, with the middle finger longer. The fingers and thumb appear straight, which su…",
      "handType": "The hand reads as earth: a square palm with fingers that are not especially long. This suggests reliability, steady habi…",
      "health": "The life line is clear and strongly curved, which can reflect steady general energy. This is a reflection only and not m…",
      "lines": [
        {
          "guidance": "Keep regular rest and movement in your routine to support steady energy.",
          "line": "Life Line",
          "means": "The life line reflects vitality and general life energy. A clear, unbroken line suggests steady energy over time.",
          "seen": "A clear, strongly curved line wraps around the thumb pad and runs toward the wrist."
        }
      ],
      "love": "The heart line is faint and long, which may point to thoughtful, measured feelings. The Venus pad looks normal, suggesti…",
      "mounts": "The mounts look fairly even in this photo. Most pads appear normal, with no strong swelling or hollowing. The upper and…",
      "overview": "This is your right hand, which is your dominant hand, so it shows how you are shaping your life now. The palm is fairly…",
      "strengths": [
        "steady and practical outlook"
      ],
      "takeaways": [
        "Name your goals and take one small step at a time."
      ],
      "timing": "The life line and fate line show no clear breaks or forks in this photo, so no particular phases of change stand out her…"
    },
    "summary": "Your right hand reads as a Earth hand: practical, grounded, reliable, traditional values, love of nature. Head Line is s…",
    "disclaimer": "Palmistry is a traditional practice offered for reflection. It is not medical, legal or financial advice.",
    "readingMode": "single",
    "photosAnalysed": 1,
    "observedHand": "right",
    "confidenceBasis": "observed",
    "localization": {
      "language": "en",
      "readingLanguage": "en",
      "fixedTextLanguage": "en",
      "referenceTextLanguage": "en"
    },
    "dataSource": "vedika-reference"
  },
  "billing": {
    "charged": 0.05,
    "currency": "USD",
    "category": "palmistry",
    "endpoint": "/v2/palmistry/ai-reading",
    "balanceBefore": 25.0,
    "balanceAfter": 24.95
  },
  "meta": {
    "engine": "vedika-intelligence",
    "source": "vedika-palmistry",
    "version": "2.1.0"
  }
}

The lists are shortened here. A full reading holds six lines (heart, head, life, fate, sun, mercury), six mounts, five fingers, minorLines and marks seen with confidence, and a reading object with these sections: overview, handType, lines, mounts, fingersAndThumb, love, careerAndMoney, health, timing, strengths, cautions and takeaways. imageQuality, handDetected and handNote tell you how well the photo read, and retakeTips lists ways to improve it.

#Photos that cannot be read

A photo that is not a palm, or that is too unclear, is refused with 422 and is never charged. The body carries data.retakeTips, a short list of ways to take a better photo. The tips are written in the request language when the reader could write them; otherwise they are fixed tips in the language you asked for. data.retakeTipsLanguage says which.

{
  "success": false,
  "code": "PALM_NOT_DETECTED",
  "error": "The photo does not show the inside of a hand. Send a clear, well-lit photo of an open palm facing the camera. You were not charged.",
  "data": {
    "retakeTips": [
      "Use bright, even daylight without flash.",
      "Hold the phone steady and make sure the palm is in focus."
    ],
    "retakeTipsLanguage": "en"
  },
  "billing": {
    "charged": 0,
    "refunded": false
  }
}

A photo with more than one hand in it is refused the same way (422 PALM_IMAGE_UNCLEAR). Send one hand per photo, and use images for two hands.

#Ask a question about a reading

POST /v2/palmistry/ask. Answers a question from a stored reading. It never looks at the photo. The answer names the lines and mounts it rests on (basedOn), says so when the palm does not show what was asked, and suggests follow-up questions.

FieldRequiredMeaning
readingIdYesThe id from ai-reading.
questionYes3 to 500 characters.
languageNoAny of the 31 codes in the language table. Hinglish and hi-Latn mean hinglish; or means od. Left out, the language of the question is detected. A Hinglish answer stays in Latin script. If an answer cannot be produced in the language asked for, the answer is in English, data.language says en and data.languageNote says why; a question in a language outside the list is answered in English with a languageNote. lang is accepted too.
historyNoEarlier messages of the chat, oldest first, each {"role": "user" or "assistant", "content": text}, up to 1,500 characters each. The latest 6 are read. Up to 50 are accepted.
curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/ask \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
  "question": "Aap meri heart line ke baare mein kya kehte hain?",
  "language": "hinglish"
}'
import os, uuid, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/ask",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
        "question": "Aap meri heart line ke baare mein kya kehte hain?",
        "language": "hinglish"
    },
    timeout=120,
)
print(r.status_code, r.json())
import { randomUUID } from "node:crypto";

const r = await fetch("https://api.vedika.io/v2/palmistry/ask", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "question": "Aap meri heart line ke baare mein kya kehte hain?",
    "language": "hinglish"
  }),
  signal: AbortSignal.timeout(120_000),
});
console.log(r.status, await r.json());
{
  "success": true,
  "data": {
    "answer": "Your Heart Line is not visible in this photo, so the palm does not show a clear picture of your romantic luck. The Mount of Venus looks normal and balanced, which suggests a warm but measured approach to affection. A retake in bright, even light may reveal more about your emotional style.",
    "basedOn": [
      {
        "key": "line:heart",
        "kind": "line",
        "name": "Heart Line",
        "visible": false
      },
      {
        "key": "mount:venus",
        "kind": "mount",
        "name": "Mount of Venus",
        "visible": true
      }
    ],
    "disclaimer": "Palmistry is a traditional practice offered for reflection. It is not medical, legal or financial advice.",
    "followUps": [
      "What does my Head Line say about me?",
      "What does my Life Line suggest?"
    ],
    "historyTurnsUsed": 0,
    "language": "en",
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "safetyNote": null,
    "status": "answered",
    "system": "palmistry",
    "type": "ask",
    "usage": {
      "cachedTokens": 0,
      "inputTokens": 2829,
      "outputTokens": 154
    }
  },
  "billing": {
    "charged": 0.01,
    "currency": "USD",
    "category": "ai-query",
    "endpoint": "/v2/palmistry/ask"
  },
  "meta": {
    "engine": "vedika-intelligence",
    "source": "vedika-palmistry",
    "version": "2.1.0"
  }
}

data.status is answered, not_shown (the palm does not show what was asked, or the question is not about the palm), declined or safety. Questions about when someone will die, or how long they will live, are declined, and a question that signals a crisis gets a safety reply. A decline, a safety reply and a not_shown reply cost nothing: billing.charged is 0. An answer costs by its tokens, see prices. The tokens used are in data.usage.

#Compare two readings

POST /v2/palmistry/compatibility. Compares two people from their stored readings: love and emotional style (heart lines), thinking and communication (head lines), and energy and pace of life (life lines and the Mount of Venus). Costs $0.02.

FieldRequiredMeaning
readingIdAYesThe first reading.
readingIdBYesThe second reading. It must be a different reading from A.
nameANoA name to use in the text. Up to 30 characters: letters in any script (Devanagari too), digits, spaces, dots, apostrophes and hyphens. No underscores or symbols such as &.
nameBNoA name to use in the text. Up to 30 characters: letters in any script (Devanagari too), digits, spaces, dots, apostrophes and hyphens. No underscores or symbols such as &.
languageNoAny of the 31 codes in the language table. Default en.
curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/compatibility \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "readingIdA": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
  "readingIdB": "palm_01K9X4P8M2H6J3S7V5Z1CDEFGA",
  "nameA": "Asha",
  "nameB": "Ravi",
  "language": "en"
}'
import os, uuid, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/compatibility",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "readingIdA": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
        "readingIdB": "palm_01K9X4P8M2H6J3S7V5Z1CDEFGA",
        "nameA": "Asha",
        "nameB": "Ravi",
        "language": "en"
    },
    timeout=120,
)
print(r.status_code, r.json())
import { randomUUID } from "node:crypto";

const r = await fetch("https://api.vedika.io/v2/palmistry/compatibility", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    "readingIdA": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "readingIdB": "palm_01K9X4P8M2H6J3S7V5Z1CDEFGA",
    "nameA": "Asha",
    "nameB": "Ravi",
    "language": "en"
  }),
  signal: AbortSignal.timeout(120_000),
});
console.log(r.status, await r.json());
{
  "success": true,
  "data": {
    "caveats": [
      "The theme \"Love and emotional style\" was not read because a needed line or mount was not clearly seen for Asha.",
      "This comparison reads only what the two stored palm readings recorded. It describes styles and tendencies, not outcomes."
    ],
    "dataSource": "vedika-reference",
    "disclaimer": "Palmistry is a traditional practice offered for reflection. It is not medical, legal or financial advice, and it says nothing abou…",
    "frictionPoints": [
      {
        "kind": "none",
        "text": "No strong friction shows in the themes read. The usual adjustments of any two people still apply.",
        "theme": null
      }
    ],
    "language": "en",
    "overall": {
      "band": "balanced",
      "basis": "Each theme read scores 3 when the two styles match, 2 when they are a step apart and 1 when they are at opposite ends. The band is…",
      "maxPoints": 6,
      "points": 5,
      "summary": "Asha and Ravi fit well overall, with a few differences that reward attention.",
      "themesAssessed": 2
    },
    "practicalAdvice": [
      {
        "text": "Invite a third view on big decisions, since two similar minds can share the same blind spot.",
        "theme": "thinking"
      },
      {
        "text": "Take turns choosing the plan, so the pace alternates between lift and ease.",
        "theme": "energy"
      }
    ],
    "readings": {
      "a": {
        "label": "Asha",
        "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
        "hand": "right",
        "handRole": "dominant"
      },
      "b": {
        "label": "Ravi",
        "readingId": "palm_01K9X4P8M2H6J3S7V5Z1CDEFGA",
        "hand": "right",
        "handRole": "dominant"
      }
    },
    "sharedStrengths": [
      {
        "relation": "aligned",
        "text": "You tend to be on the same wavelength, so decisions and conversations can move quickly.",
        "theme": "thinking"
      },
      {
        "relation": "complementary",
        "text": "Lift from one side and steadiness from the other keep the pace of life comfortable for both.",
        "theme": "energy"
      }
    ],
    "system": "palmistry",
    "themes": [
      {
        "a": {
          "evidence": "Head line: long, clearly marked, gently curved",
          "observed": {
            "breaks": 0,
            "curve": "slight",
            "depth": "clear",
            "length": "long",
            "present": true
          },
          "style": "balanced",
          "text": "For Asha, thinking is balanced: facts and feelings are both weighed, and the style adapts to the situation."
        },
        "b": {
          "evidence": "Head line: medium-length, faint, gently curved",
          "observed": {
            "breaks": 0,
            "curve": "slight",
            "depth": "faint",
            "length": "medium",
            "present": true
          },
          "style": "balanced",
          "text": "For Ravi, thinking is balanced: facts and feelings are both weighed, and the style adapts to the situation."
        },
        "key": "thinking",
        "line": "head",
        "points": 3,
        "relation": "aligned",
        "summary": "Asha and Ravi think in much the same way, so conversations tend to move quickly with little misreading.",
        "title": "Thinking and communication"
      }
    ],
    "type": "compatibility"
  },
  "billing": {
    "charged": 0.02,
    "currency": "USD",
    "category": "palmistry",
    "endpoint": "/v2/palmistry/compatibility"
  },
  "meta": {
    "engine": "vedika-intelligence",
    "source": "vedika-palmistry",
    "version": "2.1.0"
  }
}

The lists are shortened. If fewer than two themes can be read because the lines were not clearly seen, the call returns 422 PALM_COMPARISON_NOT_POSSIBLE and costs nothing.

#Set a reading beside a birth chart

POST /v2/palmistry/with-chart. Sets one stored reading beside the person's Vedic birth chart, computed with XALEN Ephemeris. It compares career (fate line and the 10th house), relationships (heart line and the 7th house) and vitality (life line and the ascendant lord), then places the current Vimshottari period against each theme. Costs $0.03.

FieldRequiredMeaning
readingIdYesThe id from ai-reading.
datetimeYesBirth date and time, for example 1990-05-15T10:30:00. The time of birth is required: without it the houses cannot be placed.
latitudeYesBirth place latitude in decimal degrees.
longitudeYesBirth place longitude in decimal degrees.
timezoneNoA zone name such as Asia/Kolkata, or an offset such as +05:30. If omitted, it is taken from the offset in datetime, or else inferred from latitude and longitude.
languageNoAny of the 31 codes in the language table. Default en.
curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/with-chart \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
  "datetime": "1990-05-15T10:30:00",
  "latitude": 28.6139,
  "longitude": 77.209,
  "timezone": "Asia/Kolkata",
  "language": "en"
}'
import os, uuid, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/with-chart",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
        "datetime": "1990-05-15T10:30:00",
        "latitude": 28.6139,
        "longitude": 77.209,
        "timezone": "Asia/Kolkata",
        "language": "en"
    },
    timeout=120,
)
print(r.status_code, r.json())
import { randomUUID } from "node:crypto";

const r = await fetch("https://api.vedika.io/v2/palmistry/with-chart", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "datetime": "1990-05-15T10:30:00",
    "latitude": 28.6139,
    "longitude": 77.209,
    "timezone": "Asia/Kolkata",
    "language": "en"
  }),
  signal: AbortSignal.timeout(120_000),
});
console.log(r.status, await r.json());
{
  "success": true,
  "data": {
    "caveats": [
      "The theme \"relationships\" was not read because a needed line or mount was not clearly seen for you."
    ],
    "chart": {
      "ascendant": {
        "lord": "Moon",
        "sign": "Cancer"
      },
      "ayanamsa": "Lahiri",
      "houseSystem": "whole_sign"
    },
    "dataSource": "vedika-ephemeris",
    "disclaimer": "Palmistry is a traditional practice offered for reflection. It is not medical, legal or financial advice, and it says nothing abou…",
    "hand": "right",
    "language": "en",
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "synthesis": {
      "agree": 0,
      "alignment": "mixed",
      "differ": 1,
      "partlyAgree": 1,
      "summary": "Palm and chart agree on some themes and differ on others. Read the differences as two angles, not a contradiction."
    },
    "system": "palmistry",
    "themes": [
      {
        "agreement": "partly_agree",
        "chart": {
          "house": 10,
          "lord": "Mars",
          "lordPlacement": {
            "dignity": "neutral",
            "house": 8,
            "sign": "Aquarius"
          },
          "occupants": [
            "Mercury"
          ],
          "sign": "Aries",
          "signal": "light"
        },
        "guidance": "Use the palm as your style and the chart as your pacing: act on the clear parts and be patient with the rest.",
        "key": "career",
        "palm": {
          "evidence": "Fate line: not clearly seen",
          "kind": "self_directed",
          "line": "fate",
          "observed": {
            "breaks": 0,
            "curve": null,
            "depth": null,
            "length": null,
            "present": false
          },
          "signal": "moderate"
        },
        "reading": "No fate line is visible, which in palmistry reads as a self-directed path without one fixed track. Your 10th house falls in Aries…",
        "timing": {
          "links": [],
          "text": "Your current periods do not directly touch the career theme, so it develops in the background."
        },
        "title": "Career: fate line and the 10th house"
      }
    ],
    "timing": {
      "antarDasha": {
        "endDate": "2027-05-23",
        "planet": "Sun",
        "startDate": "2026-06-28"
      },
      "mahaDasha": {
        "endDate": "2029-12-09",
        "planet": "Rahu",
        "startDate": "2011-12-09"
      },
      "system": "vimshottari",
      "text": "You are in the Rahu major period (Vimshottari mahadasha) from 9 Dec 2011 to 9 Dec 2029, within it the Sun sub-period from 28 Jun 2…"
    },
    "type": "with-chart"
  },
  "billing": {
    "charged": 0.03,
    "currency": "USD",
    "category": "palmistry",
    "endpoint": "/v2/palmistry/with-chart"
  },
  "meta": {
    "engine": "vedika-intelligence",
    "source": "vedika-palmistry",
    "version": "2.1.0"
  }
}

The chart side uses whole-sign houses and the Lahiri ayanamsa. The response lists these under data.chart.

#PDF report

POST /v2/palmistry/report-pdf. Returns a stored reading as a PDF, base64 encoded in data.pdfBase64. Costs $0.10, charged once after the file is made. The report is white label: with no branding it is neutral and carries no Vedika name or mark.

FieldRequiredMeaning
readingIdYesThe id from ai-reading.
langNoAny of the 31 codes in the language table. Default en. language is read as lang when lang is not sent.
nameNoA name for the cover, up to 120 characters.
brandingNo{"name", "logoBase64", "accentColor"}. Your company name, your logo (PNG, JPEG, GIF or WebP, up to 256 KB) and a hex accent colour such as #0f766e. A logo is embedded in the file, not fetched from a web address.
includeChartNoBirth details (datetime, latitude, longitude, timezone). Adds a birth chart page after the palm reading.
curl -sS --max-time 120 https://api.vedika.io/v2/palmistry/report-pdf \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF", "lang": "en", "name": "Asha Rao"}' \
  | jq -r '.data.pdfBase64' | base64 --decode > palm-report.pdf
import base64, os, uuid, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/report-pdf",
    headers={
        "Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF", "lang": "en", "name": "Asha Rao"},
    timeout=120,
)
r.raise_for_status()
with open("palm-report.pdf", "wb") as f:
    f.write(base64.b64decode(r.json()["data"]["pdfBase64"]))
import { writeFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";

const r = await fetch("https://api.vedika.io/v2/palmistry/report-pdf", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
    "Idempotency-Key": randomUUID(),
  },
  body: JSON.stringify({ readingId: "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF", lang: "en", name: "Asha Rao" }),
  signal: AbortSignal.timeout(120_000),
});
if (!r.ok) throw new Error(r.status + " " + (await r.text()));
const { data } = await r.json();
await writeFile("palm-report.pdf", Buffer.from(data.pdfBase64, "base64"));
{
  "success": true,
  "data": {
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "reportType": "palmistry-report",
    "language": "en",
    "hand": "right",
    "includesChart": false,
    "whiteLabel": true,
    "contentType": "application/pdf",
    "delivery": "inline-base64",
    "pageCount": 8,
    "pdfBytes": 240780,
    "pdfBase64": "JVBERi0xLjcK…"
  },
  "billing": {
    "charged": 0.1,
    "currency": "USD",
    "category": "palmistry",
    "endpoint": "/v2/palmistry/report-pdf"
  },
  "meta": {
    "engine": "vedika-intelligence",
    "source": "vedika-palmistry",
    "version": "2.1.0"
  }
}

If the file cannot be made the call returns 502 and costs nothing. Asking again with the same Idempotency-Key and body returns the file again and does not charge a second time.

#Delete a reading

POST /v2/palmistry/delete-reading. Deletes a stored reading and the saved copies of the same request, so a repeat of the same photo cannot bring it back. It is free. Use it when your end user asks you to erase their reading.

FieldRequiredMeaning
readingIdYesThe reading to delete.
curl -sS https://api.vedika.io/v2/palmistry/delete-reading \
  -H "Authorization: Bearer $VEDIKA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF"}'
import os, requests

r = requests.post(
    "https://api.vedika.io/v2/palmistry/delete-reading",
    headers={"Authorization": "Bearer " + os.environ["VEDIKA_KEY"]},
    json={"readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF"},
    timeout=30,
)
print(r.status_code, r.json())
const r = await fetch("https://api.vedika.io/v2/palmistry/delete-reading", {
  method: "POST",
  headers: {
    Authorization: "Bearer " + process.env.VEDIKA_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ readingId: "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF" }),
});
console.log(r.status, await r.json());
{
  "success": true,
  "data": {
    "readingId": "palm_01K9X4N2Q7R5T8V3W6Y0ZBCDEF",
    "deleted": true
  }
}

An id that does not exist, has expired or belongs to another account returns 404 PALM_READING_NOT_FOUND. After a delete, every other palm route returns that same 404 for the id. A GDPR erasure of the whole account also removes its palm readings.

#Official SDKs

The Python SDK (pip install vedika-sdk, version 3.3.0 or later) and the JavaScript SDK (npm install @vedika-io/sdk, version 3.3.0 or later) call every palm route as client.palmistry. They do the base64 encoding, wait at least 180 seconds (or your client timeout, if that is longer), send a retry key for you so a resent request is not charged twice, and check the palm_... id before anything is sent. In the table, JavaScript names are in the second column.

RoutePythonJavaScript
ai-readingread(image, hand=, images=, dominant_hand=, language=, overlay=)read({ image, hand, images, dominantHand, language, overlay })
askask(reading_id, question, language=, history=)ask({ readingId, question, language, history })
compatibilitycompatibility(reading_id_a, reading_id_b, language=, name_a=, name_b=)compatibility({ readingIdA, readingIdB, language, nameA, nameB })
with-chartwith_chart(reading_id, datetime, latitude, longitude, timezone=, language=)withChart({ readingId, datetime, latitude, longitude, timezone, language })
report-pdfreport_pdf(...) and report_pdf_bytes(reading_id, language=, name=, branding=, include_chart=)reportPdf(...) and reportPdfBytes({ readingId, language, name, branding, includeChart })
delete-readingdelete_reading(reading_id)deleteReading(readingId)
import os
from vedika import VedikaClient

client = VedikaClient(api_key=os.environ["VEDIKA_KEY"])  # a live key

# 1. Read the photo (waits up to 3 minutes by default; a retry key is sent for you)
reading = client.palmistry.read("palm.jpg", hand="right", language="en")
rid = reading.reading_id          # keep it: readings are stored for 30 days
print(reading.summary)

# Both hands: one photo each
both = client.palmistry.read(
    images=[{"image": "left.jpg", "hand": "left"}, {"image": "right.jpg", "hand": "right"}],
    dominant_hand="right",
)

# 2. Ask a question: language is any of the 31 codes, for example "hinglish"
answer = client.palmistry.ask(rid, "Meri career line kaisi hai?", language="hinglish")
print(answer.status, answer.answer)

# 3. Compare two stored readings (en or hi)
match = client.palmistry.compatibility(rid_a, rid_b, "en", name_a="Asha", name_b="Ravi")

# 4. Set a reading beside the birth chart (the datetime must include the birth time)
chart = client.palmistry.with_chart(rid, "1990-05-15T10:30:00", 28.6139, 77.2090, "+05:30", "en")

# 5. PDF report: report_pdf returns the response, report_pdf_bytes returns the file
pdf = client.palmistry.report_pdf_bytes(rid, "en", name="Asha")
with open("palm-report.pdf", "wb") as f:
    f.write(pdf)

# 6. Delete the stored reading (free)
client.palmistry.delete_reading(rid)
import { VedikaClient } from '@vedika-io/sdk';
import { writeFileSync } from 'node:fs';

const client = new VedikaClient({ apiKey: process.env.VEDIKA_KEY }); // a live key

// 1. Read the photo (waits up to 3 minutes by default; a retry key is sent for you)
const reading = await client.palmistry.read({ image: './palm.jpg', hand: 'right', language: 'en' });
const readingId = reading.readingId; // keep it: readings are stored for 30 days
console.log(reading.summary);

// Both hands: one photo each
await client.palmistry.read({
  images: [{ image: './left.jpg', hand: 'left' }, { image: './right.jpg', hand: 'right' }],
  dominantHand: 'right',
});

// 2. Ask a question: language is any of the 31 codes, for example 'hinglish'
const answer = await client.palmistry.ask({ readingId, question: 'Meri career line kaisi hai?', language: 'hinglish' });
console.log(answer.status, answer.answer);

// 3. Compare two stored readings (en or hi)
await client.palmistry.compatibility({ readingIdA, readingIdB, language: 'en', nameA: 'Asha', nameB: 'Ravi' });

// 4. Set a reading beside the birth chart (the datetime must include the birth time)
await client.palmistry.withChart({
  readingId, datetime: '1990-05-15T10:30:00', latitude: 28.6139, longitude: 77.209, timezone: '+05:30', language: 'en',
});

// 5. PDF report: reportPdf returns the response, reportPdfBytes returns the file
const pdf = await client.palmistry.reportPdfBytes({ readingId, language: 'en', name: 'Asha' });
writeFileSync('palm-report.pdf', pdf);

// 6. Delete the stored reading (free)
await client.palmistry.deleteReading(readingId);

Sandbox. The typed methods call the live routes and need a live key. A sandbox key (vk_sandbox_) gets 403 SANDBOX_KEY_SCOPE from them. To test for free, send the same bodies to the /sandbox/v2/palmistry/... routes with the SDK's raw request method:

# Sandbox: the typed methods need a live key, so send the same body to the sandbox route
import os
from vedika import VedikaClient, encode_palm_image

client = VedikaClient(api_key=os.environ["VEDIKA_SANDBOX_KEY"])  # a vk_sandbox_ key
sample = client.request("POST", "/sandbox/v2/palmistry/ai-reading", json={"image": encode_palm_image("palm.jpg")})
import { VedikaClient, encodePalmImage } from '@vedika-io/sdk';

// Sandbox: the typed methods need a live key, so send the same body to the sandbox route
const sandbox = new VedikaClient({ apiKey: process.env.VEDIKA_SANDBOX_KEY }); // a vk_sandbox_ key
const sample = await sandbox.request('POST', '/sandbox/v2/palmistry/ai-reading', {
  body: { image: encodePalmImage('./palm.jpg') },
});

Errors arrive as VedikaAPIError (a ValidationError for a 422 photo refusal) with the status and code from the error table. See the SDK pages for install and the full reference.

#Other palmistry routes

The routes above read a photo. The palmistry family also holds two older groups. Neither one looks at a photo, and neither one gives a readingId.

Reference tables. GET or POST /v2/palmistry/lines, /mounts, /fingers and /shapes return the fixed meanings of the six lines, the mounts, the finger types and the four hand shapes. $0.01 per call.

Birth-detail element readings, not photo readings. POST /v2/palmistry/palm-reading, /life-line, /heart-line, /head-line, /fate-line and /mount-analysis take birth details (datetime, latitude, longitude) and return a reading for the element of the ascendant in the birth chart. All six give the same reading for the same birth details. They do not read a hand, take no photo and do not work with ask, compatibility, with-chart or report-pdf. To read a real palm, use ai-reading. The API explorer lists these six under the same Palmistry heading.

#Languages

Every palmistry route that takes a language accepts the same 31 codes: the 30 language codes ar as bn de en es fa fr gu hi id ja kn ko ml mr ms ne od pa pt ru sa si ta te th ur vi zh, and hinglish (Hindi in Roman letters). or is read as Odia (od).

RouteLanguagesAnything else
ai-readingAll 31 codes: the 30 language codes above, and hinglish (also Hinglish, hi-Latn)400 INVALID_LANG_PARAM, no charge
askThe same 31 codes, or detected from the question400 INVALID_LANG_PARAM, no charge
compatibilityThe same 31 codes400 INVALID_LANG_PARAM, no charge
with-chartThe same 31 codes400 INVALID_LANG_PARAM, no charge
report-pdfThe same 31 codes (field lang)400 INVALID_LANG_PARAM, no charge
delete-readingNot usedNot used

Hinglish is Hindi written in Latin letters, for example Aapki Hriday Rekha gehri hai. Every route in the table answers in it.

What stays in English. Field names and enum keys (for example key, band, relation and handShape.element) stay in English in every language. Everything written for the reader follows the language you ask for: the names of lines, mounts, fingers and hand shapes and the fixed reference meanings behind them (the response says so in localization.referenceTextLanguage), the written sections of the reading, the fixed notices (disclaimer, hand notes, minor lines and marks) and the overlay notes. Retake tips follow the rule under Photos that cannot be read.

Language detection in ask. When you leave language out, the answer is written in the language of the question when it is one of the 31 codes, and in English otherwise (data.languageNote says so).

#Prices

RoutePrice
ai-reading$0.05 per reading. The same price for every language and for both hands.
Line overlay (overlay: true)Extra, billed per 1M tokens at Vedika Pro Ultra rates: $0.40 per 1M input tokens and $2.00 per 1M output tokens. The tracing adds a few hundred tokens.
askPer 1M tokens at Vedika Pro Ultra rates: $0.40 per 1M input tokens and $2.00 per 1M output tokens. The charge is rounded to whole cents, with a 1 cent minimum. A typical answer settles at $0.01.
compatibility$0.02
with-chart$0.03
report-pdf$0.10
delete-readingFree

You are charged only for a finished result. A refusal (422), any 4xx error, a 5xx error, a decline, a safety reply and a not_shown reply cost $0. The response carries a billing block with the amount charged. Account discounts apply. Prices are in USD and are drawn from your wallet.

#Limits and timeouts

LimitValue
Photo size5 MiB (5,242,880 bytes) per photo, after decoding. An error that says "5 MB" means the same limit.
Photo formatsJPEG, PNG, WebP, GIF. HEIC and HEIF are refused.
Photo encodingStandard or URL-safe base64, padded or not. A data:image/...;base64, value is fine.
Photo dimensionsAt most 12,000 pixels on a side. A larger photo is refused with 400 INVALID_INPUT, at no charge. The API reads it at a smaller size, so a phone photo is enough.
Request formatA JSON body with Content-Type: application/json.
Photos per request1, or 2 (one per hand)
Request bodyAbout 14 MB. Over that, the call returns 413 PAYLOAD_TOO_LARGE as JSON, at no charge.
Question length (ask)3 to 500 characters
Reading timeTypically 10 to 30 seconds, never more than 50 seconds.
Rate limitBy plan, see Rate limits. Over the limit, the call returns 429 with a Retry-After header.
Timeouts
Set your client timeout to at least 90 seconds for ai-reading and report-pdf. A 30 second default will cut off slow readings. A call that your client abandons may still finish on our side: send an Idempotency-Key so that your retry returns that result instead of making a second one.

#Idempotency and retries

Send an Idempotency-Key header with every paid call. The server also reads X-Idempotency-Key and X-Request-Id. Use a new key (a UUID is fine) for each new request, and the same key for a retry of that request.

You sendYou getCharge
The same key and the same bodyThe same reading data, replayed. For ai-reading that includes the same readingId. The billing block shows charged: 0, because the first call already charged you.Once
The same photo and settings, with a new keyThe same reading, as described in Same photo, same reading.A new purchase at the same price
No keyA new request every time. For ai-reading the same photo still returns the same reading.A new purchase each time

Only an identical retry is free. The same Idempotency-Key with a different body is treated as a new request and is charged again. A key is remembered for 6 hours; after that, the same key is a new request. A key whose reading you deleted cannot be used to read the photo again: it returns 409 IDEMPOTENCY_KEY_USED. Send a new key.

import os, time, uuid, requests

def read_palm(body, tries=4):
    key = str(uuid.uuid4())          # one key for every attempt of this request
    for attempt in range(tries):
        try:
            r = requests.post(
                "https://api.vedika.io/v2/palmistry/ai-reading",
                headers={"Authorization": "Bearer " + os.environ["VEDIKA_KEY"],
                         "Idempotency-Key": key},
                json=body, timeout=120)
        except requests.RequestException:
            time.sleep(2 ** attempt)
            continue
        if r.status_code in (429, 502, 503):
            time.sleep(float(r.headers.get("Retry-After", 2 ** attempt)))
            continue
        return r
    raise RuntimeError("palm reading did not finish")
import { randomUUID } from "node:crypto";

async function readPalm(body, tries = 4) {
  const key = randomUUID(); // one key for every attempt of this request
  for (let attempt = 0; attempt < tries; attempt++) {
    let r;
    try {
      r = await fetch("https://api.vedika.io/v2/palmistry/ai-reading", {
        method: "POST",
        headers: {
          Authorization: "Bearer " + process.env.VEDIKA_KEY,
          "Content-Type": "application/json",
          "Idempotency-Key": key,
        },
        body: JSON.stringify(body),
        signal: AbortSignal.timeout(120_000),
      });
    } catch {
      await new Promise((ok) => setTimeout(ok, 2 ** attempt * 1000));
      continue;
    }
    if ([429, 502, 503].includes(r.status)) {
      const wait = Number(r.headers.get("Retry-After")) || 2 ** attempt;
      await new Promise((ok) => setTimeout(ok, wait * 1000));
      continue;
    }
    return r;
  }
  throw new Error("palm reading did not finish");
}

#Sandbox

Test the whole flow without a charge. All six palm routes also exist in the sandbox: put /sandbox in front of the live path and change nothing else. They need a sandbox key (vk_sandbox_...). Without one the answer is 401 SANDBOX_KEY_REQUIRED. Nothing is ever charged in the sandbox.

RouteWhat the sandbox does
ai-readingChecks the request exactly as live does: a bad photo field gets the same 400. Then it returns one fixed sample reading with "sandbox": true and a real palm_ readingId. The photo is not read.
askReturns a fixed sample answer in en, hi or hinglish, with "sandbox": true.
compatibility, with-chart, report-pdf, delete-readingRun for real on the sandbox readings.

Sandbox readings are kept apart from live ones. A sandbox id gives 404 PALM_READING_NOT_FOUND on a live route, and a live id gives 404 on a sandbox route. The sandbox limits per key still apply: 10 requests a minute, 50 an hour and 100 a day.

The sample reading is the same every time, so use the sandbox to test your integration: request shapes, error handling, timeouts, and how your app shows a reading. Use a live key to read real photos.

#Retention and privacy

#Error codes

Every error is JSON with a code. Most also carry success: false and an error message: {"success": false, "code": "...", "error": "..."}. A missing or malformed key is refused before the route runs, with {"code", "message", "status"}, so read error or message. Routes that bill usually add billing.charged, which is 0 on an error. A few errors leave the billing block out (a bad language, bad report-pdf fields, a report-pdf reading that is not found, and authentication errors). No error below is ever charged, with or without the block.

HTTPCodeWhenFix
400INVALID_INPUTA field is missing or wrong: no photo, over 5 MiB, HEIC, a bad hand, two photos for the same hand, overlay with two photos, a question that is too short or too long, the same id for A and B, or (with-chart) birth details that are missing or out of rangeFix the field named in error
400INVALID_INPUTThe body is empty, is not valid JSON, or is not an object. Also (ai-reading): the two photos in images are the same picture ("The two photos are the same picture: send one photo of the left hand and one of the right"), or a photo is over 12,000 pixels on a sideSend a JSON object with the documented fields, and a different photo for each hand
400INVALID_LANG_PARAMA language code that the API does not supportUse a code from the language table
400PROMPT_INJECTION_BLOCKEDask: the question tries to give the model new instructionsAsk the question in plain words
400INVALID_BIRTH_DETAILSreport-pdf: includeChart is not an object, or its birth details are missing or out of rangeSend datetime (or date and time), latitude, longitude and timezone
400READING_ID_REQUIRED, NAME_INVALID, BRANDING_INVALID, BRANDING_ACCENT_INVALID, BRANDING_LOGO_INVALID, BRANDING_LOGO_UNSUPPORTEDreport-pdf: a bad request fieldFix the field named in error
401UNAUTHORIZED / INVALID_FORMAT / INVALID_API_KEYNo key (UNAUTHORIZED), a key that is not shaped like a Vedika key (INVALID_FORMAT), or a well-formed key that is unknown or revoked (INVALID_API_KEY)Send Authorization: Bearer <key>
401SANDBOX_KEY_REQUIREDA sandbox route was called without a sandbox keySend a vk_sandbox_ key, or use the live route with a live key
403SANDBOX_KEY_SCOPEA sandbox key (vk_sandbox_) was sent to a live routeAdd /sandbox in front of the path, or send a live key
403subscription_inactiveThe account has no active subscription. Not billedActivate or renew the plan
402INSUFFICIENT_BALANCEThe wallet is too low for the callAdd funds
404PALM_READING_NOT_FOUNDThe id is unknown, expired (30 days), deleted, or belongs to another accountRead the photo again with ai-reading
409IDEMPOTENCY_KEY_USEDai-reading: this Idempotency-Key was used for a reading that you have since deletedSend a new Idempotency-Key to read the photo again
409REPORT_GENERATION_IN_PROGRESSreport-pdf: the same request is still being madeRetry shortly with the same key
415UNSUPPORTED_MEDIA_TYPEThe body is not JSON: Content-Type is missing or is not application/jsonSend Content-Type: application/json
413PAYLOAD_TOO_LARGEThe body is over about 14 MBShrink the photos
422PALM_NOT_DETECTEDThe photo is not the inside of a handRetake the photo, see data.retakeTips
422PALM_IMAGE_UNCLEARThe lines cannot be traced, or the photo shows two handsRetake the photo, see data.retakeTips
422PALM_READING_INCOMPLETEThe stored reading holds too few clearly seen lines to useRead a clearer photo
422READING_INCOMPLETEreport-pdf: the stored reading has no written sections in the language asked forAsk for en, or read the photo again
422CHART_UNAVAILABLEreport-pdf: the birth chart could not be computed from includeChartCheck the birth details, or leave includeChart out
422PALM_COMPARISON_NOT_POSSIBLEFewer than two themes could be comparedRead clearer photos
429RATE_LIMIT_EXCEEDEDToo many requests for your planWait for Retry-After, then retry
502RENDER_UNAVAILABLEreport-pdf: the file could not be madeRetry with the same key
503PALM_READING_UNAVAILABLE / PALM_ASK_UNAVAILABLE / ANSWER_CHECK_UNAVAILABLENo reading or answer could be made right now, or the safety check on the written text could not run (ANSWER_CHECK_UNAVAILABLE). Nothing is chargedRetry with the same key after a short wait

A withheld answer is not an error. When the safety check withholds the text of a reading, an answer or a PDF report, the call returns 200 with data.finishReason: "filtered", data.reason: "safety_filter" and a short data.answer in place of the result (a PDF report is not made). billing.charged is 0. Check finishReason before you read the result.

On with-chart a bad birth detail is 400 INVALID_INPUT. On report-pdf a bad includeChart is 400 INVALID_BIRTH_DETAILS, with an expected_format example in the body.

See also Error codes for the codes that every route shares, and Image inputs for photo rules.