PDF reports (Node SDK)
Early access. The
lifeastroapiNode package is being prepared for npm. Until it is published, ask us for early access; everything below describes the package as it ships.
The lifeastroapi/pdf subpath turns the JSON from client.reports.* into
branded, print-ready PDFs — cover page, charts, planet and dasha tables,
narrative sections — white-labelled with your logo, colours, footer and optional
watermark.
The API itself returns JSON only. The PDF is built on your server, by this SDK. That keeps your branding and your customers' reports on your side, and it is why PDF rendering has requirements the rest of the SDK does not.
Two layers
| Layer | Functions | Needs |
|---|---|---|
| HTML | render*Html(data, branding?) | Nothing. Pure string functions with zero dependencies; run on Node, serverless, edge or in a browser. |
*Pdf(data, opts?), htmlToPdf(html, opts?) | A headless Chromium, driven through Puppeteer (see below). |
If you only call the API, or only need HTML, you can ignore everything about Puppeteer below — it is never loaded.
What you need for PDFs
Puppeteer is an optional peer dependency: it is not installed with
lifeastroapi, and is imported only when you call a *Pdf function or htmlToPdf.
Pick one of these setups.
| Setup | Install | Disk | When to use |
|---|---|---|---|
| Puppeteer (simplest) | npm install puppeteer | roughly 400–500 MB — it downloads its own Chrome | A normal server, VM or container. |
| puppeteer-core + your own Chrome | npm install puppeteer-core | roughly 10–20 MB, plus the Chrome/Chromium you already have | Chrome is already on the machine, or you install it from your OS package manager. |
| Serverless | npm install puppeteer-core @sparticuz/chromium | sized to fit a Lambda layer | AWS Lambda, Vercel functions and similar. |
Also plan for:
- Memory. Each render runs a Chromium page; allow a few hundred MB of RAM for the browser. Reuse one browser across requests rather than launching one per PDF (see Performance).
- Fonts. See Fonts — without the right fonts, Hindi and other Indic text prints as empty boxes.
- Puppeteer 24 or newer is recommended. The page footer uses a CSS page-margin box, which needs Chromium 131+; with an older browser the PDF still renders but the footer line is not printed.
- Not available on runtimes that cannot run a browser (Cloudflare Workers, the browser itself). Use the HTML layer there and convert elsewhere.
Calling a *Pdf function without Puppeteer installed throws a clear error telling
you to install it or pass your own.
Quick start
npm install lifeastroapi puppeteerimport { LifeAstro } from "lifeastroapi";
import { kundliDetailedPdf } from "lifeastroapi/pdf";
import { writeFileSync } from "node:fs";
const client = new LifeAstro({ apiKey: process.env.LIFEASTRO_API_KEY });
// 1. Fetch the report JSON
const report = await client.reports.kundliDetailed({
date: "1990-01-15", time: "10:30", tz: "Asia/Kolkata", lat: 19.076, lon: 72.8777,
name: "Arjun Mehta", place: "Mumbai, India", lang: "hi",
});
// 2. Render a branded PDF in one call
const pdf = await kundliDetailedPdf(report, {
branding: {
companyName: "Acme Astro",
logoUrl: "https://cdn.acme.com/logo.png", // or a data: URI
primaryColor: "#6b21a8",
secondaryColor: "#9333ea",
footerText: "© Acme Astro — astrology for everyone",
watermarkText: "SAMPLE", // optional diagonal watermark
},
});
writeFileSync("kundli.pdf", pdf);Available reports
Each report has a render*Html and a *Pdf form:
| Report | API method | HTML | |
|---|---|---|---|
| Kundli (lite) | reports.kundliLite | renderKundliLiteHtml | kundliLitePdf |
| Kundli (detailed) | reports.kundliDetailed | renderKundliDetailedHtml | kundliDetailedPdf |
| Brihad Kundli | reports.kundliBrihad | renderKundliBrihadHtml | kundliBrihadPdf |
| Match-making | reports.matchMaking | renderMatchMakingHtml | matchMakingPdf |
| Mangal Dosha | reports.mangalDosha | renderMangalDoshaHtml | mangalDoshaPdf |
| Dasha analysis | reports.dashaAnalysis | renderDashaAnalysisHtml | dashaAnalysisPdf |
| Numerology | reports.numerology | renderNumerologyHtml | numerologyPdf |
| Sade Sati | reports.sadeSati | renderSadeSatiHtml | sadeSatiPdf |
| Varshaphal | reports.varshaphal | renderVarshaphalHtml | varshaphalPdf |
| Horoscope (daily / weekly / monthly) | reports.horoscopeDaily … | renderHoroscopeHtml | horoscopePdf |
All render in en, hi and mr (and other locales where the report data carries
them) — pass lang when you request the report.
Brihad Kundli is the large one (well over 100 pages). kundliBrihadPdf renders
it twice so the table of contents shows real page numbers, adds a running header
and footer, and writes PDF bookmarks. It is the slowest report to produce — give it
a generous timeout and render it in a background job rather than inside a web
request. For exact page numbers in the table of contents, also install the optional
pdfjs-dist (npm install pdfjs-dist); without it the SDK falls back to an
estimate.
Names and places on the cover
Pass name and place (or boyName / girlName / boyPlace / girlPlace for
match-making) when you request the report. Without them the cover reads "Subject" /
"Boy" / "Girl" and shows the birth coordinates.
const report = await client.reports.matchMaking({
boy, girl, boyName: "Arjun", girlName: "Asha", boyPlace: "Mumbai", girlPlace: "Delhi",
});place is display text only — the chart is always computed from lat / lon.
Branding
| Field | Effect |
|---|---|
companyName | Shown on the cover. |
logoUrl | Cover logo. http(s), data:image/... or a relative URL. |
primaryColor / secondaryColor | Headings, table headers, accents. |
footerText | Line printed in the bottom margin of every page. |
watermarkText | Optional diagonal watermark. |
Precedence: the branding you pass overrides the per-account branding embedded
in the report JSON, which falls back to neutral defaults.
Performance
Launching Chromium for every PDF is slow. On a server, launch one browser and reuse it — the SDK will not close a browser you pass in:
import puppeteer from "puppeteer";
const browser = await puppeteer.launch();
const pdf = await kundliDetailedPdf(report, { branding, browser });
// ... reuse `browser` for later requests; call browser.close() on shutdownYour own Chrome (puppeteer-core)
import puppeteer from "puppeteer-core";
const browser = await puppeteer.launch({
executablePath: "/usr/bin/chromium", // macOS: "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
});
const pdf = await kundliLitePdf(report, { browser });Or let the SDK launch it: kundliLitePdf(report, { puppeteer, launchOptions: { executablePath } }).
Serverless (AWS Lambda and similar)
import chromium from "@sparticuz/chromium";
import puppeteer from "puppeteer-core";
const browser = await puppeteer.launch({
executablePath: await chromium.executablePath(),
args: chromium.args,
});
const pdf = await horoscopePdf(report, { browser });Serverless Chromium images ship without Indic fonts — bundle the Noto fonts you need with your function (see Fonts).
Docker / Linux servers
A minimal image that can render every report, including Hindi:
FROM node:22-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
chromium fonts-noto-core fonts-noto-extra \
&& rm -rf /var/lib/apt/lists/*
ENV PUPPETEER_SKIP_DOWNLOAD=true
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .with puppeteer.launch({ executablePath: "/usr/bin/chromium", args: ["--no-sandbox"] }).
--no-sandbox is needed when the container runs as root; prefer running as a
non-root user and keeping the sandbox.
Fonts
The templates ask for the Noto Sans families. Chromium does not bundle fonts, so the machine that renders the PDF must have fonts covering the scripts you use. Without them, Devanagari and other Indic text prints as empty boxes.
- Debian / Ubuntu:
apt install fonts-noto-core(addfonts-noto-extrafor the full set of Indic scripts) - macOS and Windows desktops already include Devanagari fonts.
Without Puppeteer
Bring your own renderer, or run where a browser is not available — the HTML layer has no dependencies:
import { renderMatchMakingHtml } from "lifeastroapi/pdf";
const html = renderMatchMakingHtml(matchReport, { primaryColor: "#b91c1c" });
// → your own Puppeteer/Playwright, Gotenberg, a print service, or the browser's Print dialogThe HTML is a complete, self-contained document with print CSS (A4, page margins,
footer), so any Chromium-based converter gives the same layout. The Brihad table of
contents, running header and bookmarks are added by kundliBrihadPdf and are not
part of the plain HTML.
htmlToPdf options
All *Pdf functions accept these alongside branding:
| Option | Default | Meaning |
|---|---|---|
browser | — | Reuse a launched browser. Not closed for you. |
puppeteer | — | Inject the module (puppeteer-core). |
launchOptions | — | Passed to puppeteer.launch() when the SDK launches the browser. |
format | "A4" | A3, A5, Letter, Legal, Tabloid. |
landscape | false | Landscape orientation. |
printBackground | true | Print background colours. |
margin | template's | { top, bottom, left, right }, e.g. "10mm". |
scale | 1 | Render scale, 0.1–2. |
timeoutMs | 30000 | Time allowed for the page to load. |
Security
Branding inputs are sanitized — colours are validated against a safe allowlist, the
watermark and footer are CSS-escaped, and logoUrl is restricted to http(s),
data:image/... and relative URLs. Chromium fetches logoUrl while rendering,
so if you ever pass a logo URL supplied by an end user, run Puppeteer with
restricted network egress to avoid requests to internal hosts (SSRF).
Troubleshooting
| Symptom | Cause and fix |
|---|---|
PDF rendering requires Puppeteer | Puppeteer is not installed. npm install puppeteer, or pass { puppeteer } / { browser }. |
Could not find Chrome | puppeteer-core has no browser of its own — pass executablePath. With puppeteer, run npx puppeteer browsers install chrome. |
| Hindi text shows as boxes | No Indic font on the machine. Install Noto fonts (see Fonts). |
| Browser fails to launch in Docker | Missing system libraries or the sandbox. Use the Dockerfile above. |
| No footer line on pages | The browser is older than Chromium 131. Update Puppeteer. |
| Timeouts on Brihad Kundli | It is a very large document rendered twice. Raise timeoutMs and render in a background job. |
| Cover says "Subject" | Pass name (and place) when requesting the report. |