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.
| Route | What it does | Price |
|---|---|---|
POST /v2/palmistry/ai-reading | Reads one photo, or both hands | $0.05 per reading |
POST /v2/palmistry/ask | Answers a question about a stored reading | Per 1M tokens, 1 cent minimum |
POST /v2/palmistry/compatibility | Compares two stored readings | $0.02 |
POST /v2/palmistry/with-chart | Sets one reading beside a Vedic birth chart | $0.03 |
POST /v2/palmistry/report-pdf | A branded PDF of a stored reading | $0.10 |
POST /v2/palmistry/delete-reading | Deletes a stored reading | Free |
#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.jsonimport 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
| Field | Required | Meaning |
|---|---|---|
image | Yes, or images | One 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. |
images | Yes, or image | One 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. |
hand | No | left 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. |
dominantHand | No | left or right. Default right. Sets which hand the reading treats as dominant. |
language | No | Language of the written reading. Default en. All 31 language codes in the language table work. lang and langcode are accepted too. |
overlay | No | true 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.
| Field | Required | Meaning |
|---|---|---|
readingId | Yes | The id from ai-reading. |
question | Yes | 3 to 500 characters. |
language | No | Any 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. |
history | No | Earlier 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.
| Field | Required | Meaning |
|---|---|---|
readingIdA | Yes | The first reading. |
readingIdB | Yes | The second reading. It must be a different reading from A. |
nameA | No | A 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 &. |
nameB | No | A 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 &. |
language | No | Any 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.
| Field | Required | Meaning |
|---|---|---|
readingId | Yes | The id from ai-reading. |
datetime | Yes | Birth date and time, for example 1990-05-15T10:30:00. The time of birth is required: without it the houses cannot be placed. |
latitude | Yes | Birth place latitude in decimal degrees. |
longitude | Yes | Birth place longitude in decimal degrees. |
timezone | No | A 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. |
language | No | Any 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.
| Field | Required | Meaning |
|---|---|---|
readingId | Yes | The id from ai-reading. |
lang | No | Any of the 31 codes in the language table. Default en. language is read as lang when lang is not sent. |
name | No | A name for the cover, up to 120 characters. |
branding | No | {"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. |
includeChart | No | Birth 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.pdfimport 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.
| Field | Required | Meaning |
|---|---|---|
readingId | Yes | The 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.
| Route | Python | JavaScript |
|---|---|---|
ai-reading | read(image, hand=, images=, dominant_hand=, language=, overlay=) | read({ image, hand, images, dominantHand, language, overlay }) |
ask | ask(reading_id, question, language=, history=) | ask({ readingId, question, language, history }) |
compatibility | compatibility(reading_id_a, reading_id_b, language=, name_a=, name_b=) | compatibility({ readingIdA, readingIdB, language, nameA, nameB }) |
with-chart | with_chart(reading_id, datetime, latitude, longitude, timezone=, language=) | withChart({ readingId, datetime, latitude, longitude, timezone, language }) |
report-pdf | report_pdf(...) and report_pdf_bytes(reading_id, language=, name=, branding=, include_chart=) | reportPdf(...) and reportPdfBytes({ readingId, language, name, branding, includeChart }) |
delete-reading | delete_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).
| Route | Languages | Anything else |
|---|---|---|
ai-reading | All 31 codes: the 30 language codes above, and hinglish (also Hinglish, hi-Latn) | 400 INVALID_LANG_PARAM, no charge |
ask | The same 31 codes, or detected from the question | 400 INVALID_LANG_PARAM, no charge |
compatibility | The same 31 codes | 400 INVALID_LANG_PARAM, no charge |
with-chart | The same 31 codes | 400 INVALID_LANG_PARAM, no charge |
report-pdf | The same 31 codes (field lang) | 400 INVALID_LANG_PARAM, no charge |
delete-reading | Not used | Not 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
| Route | Price |
|---|---|
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. |
ask | Per 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-reading | Free |
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
| Limit | Value |
|---|---|
| Photo size | 5 MiB (5,242,880 bytes) per photo, after decoding. An error that says "5 MB" means the same limit. |
| Photo formats | JPEG, PNG, WebP, GIF. HEIC and HEIF are refused. |
| Photo encoding | Standard or URL-safe base64, padded or not. A data:image/...;base64, value is fine. |
| Photo dimensions | At 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 format | A JSON body with Content-Type: application/json. |
| Photos per request | 1, or 2 (one per hand) |
| Request body | About 14 MB. Over that, the call returns 413 PAYLOAD_TOO_LARGE as JSON, at no charge. |
| Question length (ask) | 3 to 500 characters |
| Reading time | Typically 10 to 30 seconds, never more than 50 seconds. |
| Rate limit | By plan, see Rate limits. Over the limit, the call returns 429 with a Retry-After header. |
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 send | You get | Charge |
|---|---|---|
| The same key and the same body | The 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 key | The same reading, as described in Same photo, same reading. | A new purchase at the same price |
| No key | A 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.
- Retry on a network error, a timeout, 429, 502 and 503, with the same key. Wait the time in
Retry-Afterwhen it is present, otherwise back off with a growing delay and some jitter. - Do not retry other 4xx errors unchanged. Fix the request first. They cost nothing.
- A 422 means the photo cannot be read. Retake the photo, then send it with 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.
| Route | What the sandbox does |
|---|---|
ai-reading | Checks 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. |
ask | Returns a fixed sample answer in en, hi or hinglish, with "sandbox": true. |
compatibility, with-chart, report-pdf, delete-reading | Run 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
- Photos are never written to Vedika storage. A photo is read in memory and sent over an encrypted connection to the analysis sub-processors listed on the Trust page, who make the reading and may hold inputs for a limited period under their own terms. Vedika keeps one-way fingerprints of the photo, which cannot be turned back into the photo, for up to 30 days after the last reading of that photo, so the same photo returns the same reading. Deleting a reading removes those fingerprints. Billing records separately keep a one-way request identifier built from the whole request, including the photo and your idempotency key (or a random id when none is sent), under billing retention.
- Readings are kept for 30 days, then deleted. A reading is the lines, mounts and text that came out of the photo, not the photo itself. It is scoped to your account: another account cannot read it, even with the id.
- Delete earlier with
POST /v2/palmistry/delete-reading. It removes the reading and the saved copies of the same request. - Palmistry is offered for reflection. It is not medical, legal or financial advice, and the API does not predict death or give a diagnosis.
#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.
| HTTP | Code | When | Fix |
|---|---|---|---|
| 400 | INVALID_INPUT | A 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 range | Fix the field named in error |
| 400 | INVALID_INPUT | The 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 side | Send a JSON object with the documented fields, and a different photo for each hand |
| 400 | INVALID_LANG_PARAM | A language code that the API does not support | Use a code from the language table |
| 400 | PROMPT_INJECTION_BLOCKED | ask: the question tries to give the model new instructions | Ask the question in plain words |
| 400 | INVALID_BIRTH_DETAILS | report-pdf: includeChart is not an object, or its birth details are missing or out of range | Send datetime (or date and time), latitude, longitude and timezone |
| 400 | READING_ID_REQUIRED, NAME_INVALID, BRANDING_INVALID, BRANDING_ACCENT_INVALID, BRANDING_LOGO_INVALID, BRANDING_LOGO_UNSUPPORTED | report-pdf: a bad request field | Fix the field named in error |
| 401 | UNAUTHORIZED / INVALID_FORMAT / INVALID_API_KEY | No 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> |
| 401 | SANDBOX_KEY_REQUIRED | A sandbox route was called without a sandbox key | Send a vk_sandbox_ key, or use the live route with a live key |
| 403 | SANDBOX_KEY_SCOPE | A sandbox key (vk_sandbox_) was sent to a live route | Add /sandbox in front of the path, or send a live key |
| 403 | subscription_inactive | The account has no active subscription. Not billed | Activate or renew the plan |
| 402 | INSUFFICIENT_BALANCE | The wallet is too low for the call | Add funds |
| 404 | PALM_READING_NOT_FOUND | The id is unknown, expired (30 days), deleted, or belongs to another account | Read the photo again with ai-reading |
| 409 | IDEMPOTENCY_KEY_USED | ai-reading: this Idempotency-Key was used for a reading that you have since deleted | Send a new Idempotency-Key to read the photo again |
| 409 | REPORT_GENERATION_IN_PROGRESS | report-pdf: the same request is still being made | Retry shortly with the same key |
| 415 | UNSUPPORTED_MEDIA_TYPE | The body is not JSON: Content-Type is missing or is not application/json | Send Content-Type: application/json |
| 413 | PAYLOAD_TOO_LARGE | The body is over about 14 MB | Shrink the photos |
| 422 | PALM_NOT_DETECTED | The photo is not the inside of a hand | Retake the photo, see data.retakeTips |
| 422 | PALM_IMAGE_UNCLEAR | The lines cannot be traced, or the photo shows two hands | Retake the photo, see data.retakeTips |
| 422 | PALM_READING_INCOMPLETE | The stored reading holds too few clearly seen lines to use | Read a clearer photo |
| 422 | READING_INCOMPLETE | report-pdf: the stored reading has no written sections in the language asked for | Ask for en, or read the photo again |
| 422 | CHART_UNAVAILABLE | report-pdf: the birth chart could not be computed from includeChart | Check the birth details, or leave includeChart out |
| 422 | PALM_COMPARISON_NOT_POSSIBLE | Fewer than two themes could be compared | Read clearer photos |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests for your plan | Wait for Retry-After, then retry |
| 502 | RENDER_UNAVAILABLE | report-pdf: the file could not be made | Retry with the same key |
| 503 | PALM_READING_UNAVAILABLE / PALM_ASK_UNAVAILABLE / ANSWER_CHECK_UNAVAILABLE | No reading or answer could be made right now, or the safety check on the written text could not run (ANSWER_CHECK_UNAVAILABLE). Nothing is charged | Retry 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.