Generate Kundali PDF Reports with Vedika API

Generate Kundali PDF Reports with Vedika API
Published: March 13, 2026 | By Vedika Intelligence | Reading time: 15 minutes

A downloadable Kundali PDF report is the single highest-perceived-value feature you can add to an astrology app. Users share PDF reports with family members, astrologers, and matrimony contacts. A polished report with planets table, Dasha timeline, Yoga analysis, and AI-generated narrative turns a basic birth chart lookup into a product people pay for.

This guide walks through the complete pipeline: fetching structured data from Vedika API, generating a professional PDF with Node.js PDFKit (server-side) and jsPDF (browser-side), handling Hindi/English multi-language content, and delivering the report by email.

What You Will Build: A Node.js endpoint that accepts birth details, fetches birth chart, Dasha, and AI narrative from Vedika API in parallel, generates a multi-section Kundali PDF with PDFKit, and returns the PDF buffer — ready to download or email.

Report Sections Overview

A professional Kundali report contains these sections in order:

Step 1: Fetch All Data in Parallel

// data-fetcher.js const VEDIKA_API = 'https://api.vedika.io'; const API_KEY = 'vk_live_YOUR_KEY_HERE'; const apiPost = async (path, body) => { const res = await fetch(`${VEDIKA_API}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': API_KEY }, body: JSON.stringify(body) }); if (!res.ok) throw new Error(`${path} failed: ${res.status}`); return res.json(); }; async function fetchKundaliData(birthDetails, language = 'en') { const { datetime, latitude, longitude, timezone, name } = birthDetails; const basePayload = { datetime, latitude, longitude, timezone }; // Fetch all data in parallel — 3 API calls, runs concurrently const [birthChart, dasha, aiReading] = await Promise.all([ // Birth chart: planetary positions, houses, dignity apiPost('/v2/astrology/birth-chart', basePayload), // Dasha timeline apiPost('/v2/astrology/vimshottari-dasha', basePayload), // AI narrative — comprehensive birth chart reading apiPost('/api/vedika/chat', { question: language === 'hi' ? 'मेरे जन्म कुंडली का विस्तृत विश्लेषण करें। ग्रह स्थिति, योग, और दशा के बारे में बताएं।' : 'Give a comprehensive analysis of my birth chart — planetary positions, active yogas, doshas, and what this chart means for career, relationships, and overall life trajectory.', birthDetails: basePayload }) ]); return { name, birthDetails, planets: birthChart.data.planets, houses: birthChart.data.houses, ascendant: birthChart.data.ascendant, activeYogas: birthChart.data.yogas || [], doshas: birthChart.data.doshas || {}, currentDasha: dasha.data.currentDasha, dashaTimeline: dasha.data.timeline, aiNarrative: aiReading.answer, remedies: birthChart.data.remedies || [] }; }

Step 2: Generate PDF with Node.js PDFKit

Install PDFKit:

npm install pdfkit # For Hindi support, also install a Unicode font # Download NotoSansDevanagari-Regular.ttf from Google Fonts

Complete PDF Generator

// kundali-pdf-generator.js const PDFDocument = require('pdfkit'); const PLANETS_ORDER = [ 'Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn', 'Rahu', 'Ketu' ]; async function generateKundaliPDF(kundaliData, language = 'en') { return new Promise((resolve, reject) => { const doc = new PDFDocument({ size: 'A4', margins: { top: 50, bottom: 50, left: 60, right: 60 }, info: { Title: `Kundali Report — ${kundaliData.name}`, Author: 'Vedika Intelligence', Subject: 'Vedic Birth Chart Analysis' } }); const buffers = []; doc.on('data', chunk => buffers.push(chunk)); doc.on('end', () => resolve(Buffer.concat(buffers))); doc.on('error', reject); // Register Hindi font if needed if (language === 'hi') { doc.registerFont('Devanagari', './fonts/NotoSansDevanagari-Regular.ttf'); doc.registerFont('Devanagari-Bold', './fonts/NotoSansDevanagari-Bold.ttf'); } const regularFont = language === 'hi' ? 'Devanagari' : 'Helvetica'; const boldFont = language === 'hi' ? 'Devanagari-Bold' : 'Helvetica-Bold'; // ─── COVER PAGE ──────────────────────────────── doc.rect(0, 0, doc.page.width, 180).fill('#7c3aed'); doc.fillColor('white') .font(boldFont).fontSize(28) .text(language === 'hi' ? 'जन्म कुंडली' : 'Kundali Report', 60, 60); doc.fontSize(16).text(kundaliData.name, 60, 100); doc.fontSize(11).text('Powered by Vedika Intelligence · vedika.io', 60, 130); // Birth details box doc.fillColor('#1a1a2e') .font(boldFont).fontSize(13) .text(language === 'hi' ? 'जन्म विवरण' : 'Birth Details', 60, 200); const { datetime, latitude, longitude } = kundaliData.birthDetails; doc.font(regularFont).fontSize(11).fillColor('#333') .text(`Date & Time: ${new Date(datetime).toLocaleString('en-IN')}`, 60, 220) .text(`Location: ${latitude.toFixed(4)}°N, ${longitude.toFixed(4)}°E`, 60, 235) .text(`Ascendant: ${kundaliData.ascendant.sign} ${kundaliData.ascendant.degree.toFixed(2)}°`, 60, 250); // ─── PLANETARY POSITIONS TABLE ───────────────── doc.addPage(); drawSectionHeader(doc, boldFont, language === 'hi' ? 'ग्रह स्थिति' : 'Planetary Positions'); const tableTop = 100; const colWidths = [80, 90, 45, 50, 80, 70]; const colLabels = ['Planet', 'Sign', 'House', 'Degree', 'Dignity', 'Status']; drawTableHeader(doc, boldFont, tableTop, colWidths, colLabels); let rowY = tableTop + 25; PLANETS_ORDER.forEach((planet, i) => { const p = kundaliData.planets[planet.toLowerCase()]; if (!p) return; const rowBg = i % 2 === 0 ? '#f8f9fa' : 'white'; doc.rect(60, rowY - 3, 475, 20).fill(rowBg); doc.fillColor('#333').font(regularFont).fontSize(10); const rowData = [ planet, p.sign, p.house.toString(), p.degree.toFixed(2) + '°', p.dignity || 'Neutral', p.isRetrograde ? 'Retrograde' : p.isCombust ? 'Combust' : 'Direct' ]; let x = 60; rowData.forEach((cell, ci) => { doc.text(cell, x + 4, rowY, { width: colWidths[ci] - 8 }); x += colWidths[ci]; }); rowY += 20; }); // ─── DASHA TIMELINE ──────────────────────────── doc.addPage(); drawSectionHeader(doc, boldFont, language === 'hi' ? 'विंशोत्तरी दशा' : 'Vimshottari Dasha Timeline'); const { mahadasha, antardasha } = kundaliData.currentDasha; doc.font(boldFont).fontSize(12).fillColor('#7c3aed') .text(`Current Mahadasha: ${mahadasha.planet} (until ${mahadasha.endDate})`, 60, 100); doc.font(regularFont).fillColor('#555') .text(`Current Antardasha: ${antardasha.planet} (until ${antardasha.endDate})`, 60, 118); // Draw 5-year Dasha bars let dashaY = 145; kundaliData.dashaTimeline.slice(0, 8).forEach(d => { const isCurrent = d.planet === mahadasha.planet; doc.rect(60, dashaY, 475, 22).fill(isCurrent ? '#ede9fe' : '#f8f9fa'); doc.fillColor(isCurrent ? '#7c3aed' : '#333') .font(isCurrent ? boldFont : regularFont).fontSize(10) .text(`${d.planet} Mahadasha`, 65, dashaY + 6, { width: 150 }) .text(`${d.startDate} → ${d.endDate}`, 220, dashaY + 6) .text(`${d.years} years`, 430, dashaY + 6); dashaY += 26; }); // ─── ACTIVE YOGAS ────────────────────────────── if (kundaliData.activeYogas.length > 0) { doc.addPage(); drawSectionHeader(doc, boldFont, language === 'hi' ? 'सक्रिय योग' : 'Active Yogas'); let yogaY = 100; kundaliData.activeYogas.forEach(yoga => { doc.font(boldFont).fontSize(11).fillColor('#1a1a2e') .text(yoga.name, 60, yogaY); doc.font(regularFont).fontSize(10).fillColor('#555') .text(yoga.description || yoga.meaning || '', 60, yogaY + 14, { width: 475 }); yogaY += 45; if (yogaY > 720) { doc.addPage(); yogaY = 60; } }); } // ─── AI NARRATIVE ────────────────────────────── doc.addPage(); drawSectionHeader(doc, boldFont, language === 'hi' ? 'विस्तृत विश्लेषण' : 'Detailed Analysis'); doc.font(regularFont).fontSize(11).fillColor('#333') .text(kundaliData.aiNarrative, 60, 100, { width: 475, align: 'justify', lineGap: 4 }); // ─── FOOTER ON EACH PAGE ─────────────────────── const pageCount = doc.bufferedPageRange().count; for (let i = 0; i < pageCount; i++) { doc.switchToPage(i); doc.fontSize(8).fillColor('#aaa') .text(`Page ${i+1} of ${pageCount} · Generated by Vedika Intelligence · vedika.io`, 60, doc.page.height - 40, { align: 'center', width: 475 }); } doc.end(); }); } function drawSectionHeader(doc, boldFont, title) { doc.rect(60, 65, 475, 3).fill('#7c3aed'); doc.font(boldFont).fontSize(16).fillColor('#1a1a2e').text(title, 60, 75); } function drawTableHeader(doc, boldFont, y, colWidths, labels) { doc.rect(60, y - 5, 475, 22).fill('#7c3aed'); doc.font(boldFont).fontSize(10).fillColor('white'); let x = 60; labels.forEach((label, i) => { doc.text(label, x + 4, y, { width: colWidths[i] - 8 }); x += colWidths[i]; }); }

Step 3: Express API Endpoint

// routes/kundali-report.js const express = require('express'); const router = express.Router(); router.post('/generate-kundali-pdf', async (req, res) => { try { const { name, datetime, latitude, longitude, timezone, language = 'en' } = req.body; if (!name || !datetime || !latitude || !longitude) { return res.status(400).json({ error: 'Missing required fields' }); } // Fetch all Vedika data const kundaliData = await fetchKundaliData( { name, datetime, latitude, longitude, timezone: timezone || '+05:30' }, language ); // Generate PDF buffer const pdfBuffer = await generateKundaliPDF(kundaliData, language); // Send as download const filename = `kundali-${name.replace(/\s+/g, '-').toLowerCase()}.pdf`; res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); res.send(pdfBuffer); } catch (err) { console.error('PDF generation error:', err); res.status(500).json({ error: 'Failed to generate Kundali PDF' }); } }); module.exports = router;

Step 4: Browser-Side Generation with jsPDF

For a "Download PDF" button in a web app without a server round-trip, use jsPDF with the autotable plugin:

<!-- Include in HTML --> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js"></script>
// browser-pdf.js async function downloadKundaliPDF(birthDetails) { // 1. Fetch data from your backend (which calls Vedika API) const data = await fetch('/api/kundali-data', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(birthDetails) }).then(r => r.json()); // 2. Create jsPDF document const { jsPDF } = window.jspdf; const doc = new jsPDF({ orientation: 'portrait', unit: 'mm', format: 'a4' }); // 3. Cover page doc.setFillColor(124, 58, 237); doc.rect(0, 0, 210, 60, 'F'); doc.setTextColor(255, 255, 255); doc.setFontSize(24); doc.setFont('helvetica', 'bold'); doc.text('Kundali Report', 20, 30); doc.setFontSize(14); doc.text(data.name, 20, 45); // 4. Planetary positions table doc.setTextColor(0); doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text('Planetary Positions', 20, 80); const planetRows = Object.entries(data.planets).map(([planet, p]) => [ planet.charAt(0).toUpperCase() + planet.slice(1), p.sign, p.house, p.degree.toFixed(2) + '°', p.dignity || 'Neutral', p.isRetrograde ? 'Rx' : p.isCombust ? 'Combust' : 'Direct' ]); doc.autoTable({ head: [['Planet', 'Sign', 'House', 'Degree', 'Dignity', 'Status']], body: planetRows, startY: 85, headStyles: { fillColor: [124, 58, 237], textColor: 255 }, alternateRowStyles: { fillColor: [248, 249, 250] }, styles: { fontSize: 10, cellPadding: 3 } }); // 5. AI narrative on new page doc.addPage(); doc.setFontSize(14); doc.setFont('helvetica', 'bold'); doc.text('Chart Analysis', 20, 20); doc.setFont('helvetica', 'normal'); doc.setFontSize(10); const narrativeLines = doc.splitTextToSize(data.aiNarrative, 170); doc.text(narrativeLines, 20, 30); // 6. Trigger download doc.save(`kundali-${data.name.replace(/\s+/g, '-').toLowerCase()}.pdf`); }

Step 5: Email Delivery with Nodemailer

// email-report.js const nodemailer = require('nodemailer'); async function emailKundaliReport(toEmail, name, pdfBuffer) { const transporter = nodemailer.createTransport({ host: process.env.SMTP_HOST, port: 587, auth: { user: process.env.SMTP_USER, pass: process.env.SMTP_PASS } }); await transporter.sendMail({ from: '"Vedika Astrology" <reports@yourapp.com>', to: toEmail, subject: `Your Kundali Report — ${name}`, html: ` <p>Dear ${name},</p> <p>Your personalised Kundali report is attached. It includes:</p> <ul> <li>Complete planetary positions with dignity and status</li> <li>Vimshottari Dasha timeline</li> <li>Active Yogas in your chart</li> <li>AI-powered personalised interpretation</li> </ul> <p>Powered by Vedika Intelligence.</p> `, attachments: [{ filename: `kundali-${name.replace(/\s+/g, '-').toLowerCase()}.pdf`, content: pdfBuffer, contentType: 'application/pdf' }] }); } // Complete flow in one function async function generateAndEmailReport(req, res) { const { name, email, datetime, latitude, longitude, timezone, language } = req.body; const kundaliData = await fetchKundaliData({ name, datetime, latitude, longitude, timezone }, language); const pdfBuffer = await generateKundaliPDF(kundaliData, language); await emailKundaliReport(email, name, pdfBuffer); res.json({ success: true, message: `Report sent to ${email}` }); }

Build Your Kundali Report Generator

Vedika API's structured JSON output maps directly to PDF layouts. Get your API key and start building today.

Get API Key

Advanced: Multi-Language Hindi Reports

For Hindi PDF reports, two things must align: the font and the API response language. Vedika AI returns responses in Hindi when you pass a Hindi question or explicitly request Hindi:

// Hindi report: pass language='hi' to fetchKundaliData // The AI question becomes Hindi → the aiNarrative comes back in Devanagari // PDFKit renders it correctly with NotoSansDevanagari font // Planet name translations for Hindi table headers const PLANET_NAMES_HI = { sun: 'सूर्य', moon: 'चन्द्र', mars: 'मंगल', mercury: 'बुध', jupiter: 'गुरु', venus: 'शुक्र', saturn: 'शनि', rahu: 'राहु', ketu: 'केतु' }; const SIGN_NAMES_HI = [ 'मेष', 'वृष', 'मिथुन', 'कर्क', 'सिंह', 'कन्या', 'तुला', 'वृश्चिक', 'धनु', 'मकर', 'कुम्भ', 'मीन' ];

Why Vedika API for Kundali Reports

Structured JSON Output

Every endpoint returns clean, typed JSON. Planets come with sign, house, degree, dignity, retrograde, and combust flags — exactly what PDF table layouts need.

AI Narrative in One Call

No need to write interpretation logic. The AI endpoint returns a 400–800 word personalised reading per chart that drops directly into the PDF narrative section.

30 Languages

Hindi, Tamil, Telugu, Bengali, Gujarati, Marathi, Kannada reports from the same API — just change the language parameter in your question.

Swiss Ephemeris Precision

Planetary positions used in the report are calculated using astronomical-grade Swiss Ephemeris. Professional astrologers will not find errors.

140+ Endpoints

Add Ashtakavarga tables, Divisional Charts (D9, D10), Shadbala scores, Panchang, or Muhurta sections to your PDF without changing providers.

$12/month

Starter plan covers all endpoints. A complete 8-section Kundali report costs a tiny fraction of a cent in API credits — the cost is in your subscription, not per call.

Conclusion

A professional Kundali PDF report is a high-perceived-value feature that differentiates your app from free web calculators. With Vedika API returning structured birth chart data, Dasha timelines, and AI narrative in parallel calls, and Node.js PDFKit handling the document generation, you can build a complete Kundali report pipeline in a single afternoon.

The same architecture extends to yearly predictions, compatibility reports, Muhurta selection reports, and Navamsa chart analysis — all with the same Vedika API subscription and the same PDF template.

Next steps:

  1. Get your API key at vedika.io/dashboard
  2. Test birth chart data in the free sandbox
  3. Read the Birth Chart API reference
  4. Add Dasha endpoint for the timeline section
  5. Use the Ashtakavarga endpoint for a premium report tier

About Vedika Intelligence: Vedika is the only B2B astrology API with AI-powered conversational capabilities, 140+ Vedic and Western calculation endpoints, and Swiss Ephemeris precision. Starting at $12/month, supporting 30 languages, used by production apps worldwide.

Try the #1 Vedic Astrology API

Birth chart JSON, Dasha timelines, AI narrative — everything you need for Kundali PDFs. Free sandbox included.

Get Free API Key