HTML ke PDF API: Cara Membuat Invoice PDF Otomatis dari Template HTML
Oleh Tim APIPedia, Tim Engineering APIPedia · Terbit · 11 menit baca
API HTML ke PDF APIPedia mengubah template HTML jadi invoice PDF lewat satu request POST /v1/pdf/html. Ada contoh curl, Node.js, dan Python.

API HTML ke PDF APIPedia mengubah template HTML menjadi berkas PDF lewat satu request POST /v1/pdf/html, sehingga invoice bisa dibuat otomatis setiap kali pesanan dibayar. Anda mengirim HTML maksimal 2 MB, memilih ukuran kertas dan margin, lalu menerima PDF yang teksnya bisa diseleksi dan dicari.
Tutorial ini memandu Anda membuat invoice PDF dari data pesanan: menyusun template yang cocok dengan mesin render, mengirim request dengan curl, Node.js, dan Python, membaca respons, menambahkan nomor halaman, dan menghindari kesalahan yang paling sering terjadi. Semua contoh memakai kunci apk_xxx dan data fiktif.
Ringkasan cepat: apa yang perlu Anda tahu?
API HTML ke PDF punya satu endpoint, menerima JSON, dan mengembalikan berkas PDF atau base64. Tabel berikut merangkum hal yang paling sering dicari.
| Item | Detail |
|---|---|
| Endpoint | POST https://api.apipedia.id/v1/pdf/html |
| Autentikasi | Header Authorization: Bearer apk_xxx |
| Input | Tepat satu dari html (maks. 2 MB) atau url (alamat http/https publik) |
| Ukuran kertas | A4 (default), Letter, Legal, A3, A5; landscape untuk orientasi lanskap |
| Margin | Objek top, right, bottom, left dalam px, in, cm, atau mm; default 10mm |
| Output | Berkas application/pdf, atau JSON berisi base64 lewat output=base64 |
| Batas | Render 20 detik, 100 halaman, PDF maksimal 20 MB |
| Cakupan CSS | Style inline dan blok <style>; flexbox, CSS Grid, stylesheet eksternal, dan JavaScript tidak didukung |
Masalah apa yang diselesaikan API HTML ke PDF?
API ini menghilangkan kebutuhan memasang dan merawat mesin pembuat PDF di server Anda sendiri. Toko online, aplikasi kasir, dan sistem langganan hampir selalu perlu mengirim invoice atau kuitansi. Membuatnya dengan tangan tidak mungkin, sedangkan menjalankan pustaka PDF sendiri berarti mengurus font, memori, dan antrean render.
Dengan API, backend Anda cukup menyusun HTML dari data pesanan dan mengirimnya dalam satu request. Hasilnya dibangun dari teks, bukan tangkapan layar, jadi nomor invoice dan nama pelanggan tetap bisa dicari di dalam PDF.

Apa yang perlu disiapkan sebelum mulai?
Anda hanya butuh satu kunci API APIPedia. Daftar atau masuk di console.apipedia.id, buka menu Token API, lalu buat kunci baru. Kunci berawalan apk_ dan dikirim lewat header Authorization: Bearer apk_xxx.
Simpan kunci di variabel lingkungan server, misalnya APIPEDIA_KEY. Jangan menaruhnya di kode yang berjalan di browser, karena siapa pun bisa membacanya. Produk HTML to PDF tercatat di tier Free pada katalog, jadi Anda bisa mencoba tanpa biaya.
Langkah 1: bagaimana menyusun template invoice yang aman dirender?
Template yang aman memakai tabel untuk tata letak dan menaruh semua CSS di blok <style> atau atribut style. Mesin render API ini bukan browser. Flexbox, CSS Grid, dan <link rel="stylesheet"> tidak dibaca, dan JavaScript tidak pernah dijalankan.
Simpan contoh berikut sebagai invoice.html. Bagian kop memakai tabel dua kolom sebagai pengganti flexbox, dan kolom angka diratakan ke kanan dengan kelas .r.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Invoice INV-2026-0042</title>
<style>
body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; color: #1c2a23; }
h1 { font-size: 22px; color: #2e7d5b; margin: 0 0 4px; }
table { width: 100%; border-collapse: collapse; }
th { background: #e3f1e8; text-align: left; padding: 8px; border-bottom: 1px solid #2e7d5b; }
td { padding: 8px; border-bottom: 1px solid #e6e2d8; }
.r { text-align: right; }
.polos td { border: 0; padding: 0 0 16px; }
.total td { font-weight: bold; border-top: 2px solid #1c2a23; }
</style>
</head>
<body>
<table class="polos">
<tr>
<td><h1>INVOICE</h1>Toko Contoh Sejahtera<br>Jl. Contoh No. 1, Bandung</td>
<td class="r">No: INV-2026-0042<br>Tanggal: 12 November 2026<br>Jatuh tempo: 19 November 2026</td>
</tr>
</table>
<p>Ditagihkan kepada: <strong>PT Pelanggan Fiktif</strong></p>
<table>
<thead>
<tr><th>Deskripsi</th><th class="r">Qty</th><th class="r">Harga</th><th class="r">Subtotal</th></tr>
</thead>
<tbody>
<tr><td>Kaos polos hitam L</td><td class="r">3</td><td class="r">Rp 85.000</td><td class="r">Rp 255.000</td></tr>
<tr><td>Topi kanvas</td><td class="r">2</td><td class="r">Rp 60.000</td><td class="r">Rp 120.000</td></tr>
<tr><td>Ongkos kirim</td><td class="r">1</td><td class="r">Rp 18.000</td><td class="r">Rp 18.000</td></tr>
<tr class="total"><td colspan="3" class="r">Total</td><td class="r">Rp 393.000</td></tr>
</tbody>
</table>
</body>
</html>

Aturan gambar dan logo
Logo harus berupa JPEG atau PNG yang bisa diakses lewat https publik. Batasnya 5 MB per gambar dan 50 gambar per dokumen. Gambar yang gagal dimuat dilewati tanpa menggagalkan render, jadi periksa hasil PDF pertama Anda untuk memastikan logo benar-benar muncul.
Langkah 2: bagaimana mengirim HTML ke API dengan curl?
Kirim isi berkas HTML sebagai field html dalam body JSON, lalu simpan respons sebagai berkas PDF. Perintah di bawah memakai jq untuk membungkus invoice.html menjadi JSON yang valid, sehingga tanda kutip di dalam HTML tidak perlu di-escape manual.
jq -n --rawfile html invoice.html '{
html: $html,
format: "A4",
margin: { top: "20mm", bottom: "20mm", left: "15mm", right: "15mm" },
filename: "invoice-INV-2026-0042.pdf"
}' | curl -s -X POST https://api.apipedia.id/v1/pdf/html \
-H "Authorization: Bearer apk_xxx" \
-H "Content-Type: application/json" \
-d @- -o invoice-INV-2026-0042.pdf
Secara default, respons berupa berkas application/pdf dengan header Content-Disposition: attachment. Buka invoice-INV-2026-0042.pdf untuk melihat hasilnya.
Parameter body yang tersedia
| Field | Tipe | Keterangan |
|---|---|---|
html | string | Dokumen atau potongan HTML, maksimal 2 MB. Isi ini atau url, tidak keduanya. |
url | string | Alamat http/https publik yang akan dirender. Alamat privat dan internal ditolak. |
format | string | A4 (default), Letter, Legal, A3, atau A5. |
landscape | boolean | Orientasi lanskap. |
margin | object | top, right, bottom, left dengan satuan px, in, cm, atau mm. Default 10mm. |
filename | string | Nama berkas hasil unduhan. |
displayHeaderFooter | boolean | Mengaktifkan header dan footer. |
headerTemplate, footerTemplate | string | Isi header dan footer, maksimal 20 KB masing-masing. |
output | string | pdf (default) atau base64. Bisa juga dikirim sebagai query ?output=base64. |
Langkah 3: bagaimana membuat invoice dari data pesanan di Node.js?
Di Node.js, susun HTML dari objek pesanan, kirim dengan fetch, lalu tulis respons biner ke berkas. Contoh ini berjalan di Node.js 18 ke atas tanpa dependensi tambahan. Fungsi esc meng-escape teks dari pengguna agar nama produk yang mengandung tanda < tidak merusak template.
import { writeFile } from "node:fs/promises";
const API = "https://api.apipedia.id/v1/pdf/html";
const KEY = process.env.APIPEDIA_KEY; // apk_xxx
const rupiah = (n) => "Rp " + n.toLocaleString("id-ID");
const esc = (s) =>
String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c]);
function invoiceHtml(order) {
const baris = order.item
.map(
(i) => `<tr><td>${esc(i.nama)}</td><td class="r">${i.qty}</td>
<td class="r">${rupiah(i.harga)}</td><td class="r">${rupiah(i.qty * i.harga)}</td></tr>`
)
.join("");
const total = order.item.reduce((n, i) => n + i.qty * i.harga, 0);
return `<!doctype html><html><head><meta charset="utf-8"><style>
body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; color: #1c2a23; }
table { width: 100%; border-collapse: collapse; }
th { background: #e3f1e8; text-align: left; padding: 8px; }
td { padding: 8px; border-bottom: 1px solid #e6e2d8; }
.r { text-align: right; }
.total td { font-weight: bold; border-top: 2px solid #1c2a23; }
</style></head><body>
<h1>INVOICE ${esc(order.nomor)}</h1>
<p>Ditagihkan kepada: <strong>${esc(order.pembeli)}</strong></p>
<table>
<thead><tr><th>Deskripsi</th><th class="r">Qty</th><th class="r">Harga</th><th class="r">Subtotal</th></tr></thead>
<tbody>${baris}
<tr class="total"><td colspan="3" class="r">Total</td><td class="r">${rupiah(total)}</td></tr>
</tbody>
</table>
</body></html>`;
}
export async function buatInvoicePdf(order) {
const filename = `invoice-${order.nomor}.pdf`;
const res = await fetch(API, {
method: "POST",
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
html: invoiceHtml(order),
format: "A4",
margin: { top: "20mm", bottom: "20mm", left: "15mm", right: "15mm" },
filename,
}),
});
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(`HTTP ${res.status}: ${err.message ?? "gagal membuat PDF"}`);
}
await writeFile(filename, Buffer.from(await res.arrayBuffer()));
return filename;
}
const berkas = await buatInvoicePdf({
nomor: "INV-2026-0042",
pembeli: "PT Pelanggan Fiktif",
item: [
{ nama: "Kaos polos hitam L", qty: 3, harga: 85000 },
{ nama: "Topi kanvas", qty: 2, harga: 60000 },
{ nama: "Ongkos kirim", qty: 1, harga: 18000 },
],
});
console.log("Tersimpan:", berkas);
Simpan sebagai invoice.mjs, lalu jalankan dengan APIPEDIA_KEY=apk_xxx node invoice.mjs. Dalam aplikasi nyata, panggil buatInvoicePdf dari handler webhook pembayaran atau dari antrean tugas, bukan dari request halaman.
Langkah 4: bagaimana menerima PDF sebagai base64 di Python?
Tambahkan output=base64 agar respons berupa amplop JSON berisi PDF dalam base64. Bentuk ini praktis bila Anda ingin menyimpan hasil ke database, mengunggahnya ke object storage, atau melampirkannya ke email tanpa menulis berkas sementara.
import base64
import os
from html import escape
import requests # pip install requests
API = "https://api.apipedia.id/v1/pdf/html"
KEY = os.environ["APIPEDIA_KEY"] # apk_xxx
CSS = """
body { font-family: Helvetica, Arial, sans-serif; font-size: 12px; color: #1c2a23; }
table { width: 100%; border-collapse: collapse; }
th { background: #e3f1e8; text-align: left; padding: 8px; }
td { padding: 8px; border-bottom: 1px solid #e6e2d8; }
.r { text-align: right; }
.total td { font-weight: bold; border-top: 2px solid #1c2a23; }
"""
def rupiah(n):
return "Rp " + f"{n:,}".replace(",", ".")
def invoice_html(order):
baris = "".join(
f"<tr><td>{escape(i['nama'])}</td><td class='r'>{i['qty']}</td>"
f"<td class='r'>{rupiah(i['harga'])}</td>"
f"<td class='r'>{rupiah(i['qty'] * i['harga'])}</td></tr>"
for i in order["item"]
)
total = sum(i["qty"] * i["harga"] for i in order["item"])
return (
"<!doctype html><html><head><meta charset='utf-8'><style>" + CSS + "</style></head><body>"
f"<h1>INVOICE {escape(order['nomor'])}</h1>"
f"<p>Ditagihkan kepada: <strong>{escape(order['pembeli'])}</strong></p>"
"<table><thead><tr><th>Deskripsi</th><th class='r'>Qty</th>"
"<th class='r'>Harga</th><th class='r'>Subtotal</th></tr></thead><tbody>"
+ baris
+ f"<tr class='total'><td colspan='3' class='r'>Total</td><td class='r'>{rupiah(total)}</td></tr>"
"</tbody></table></body></html>"
)
def buat_invoice_pdf(order):
res = requests.post(
API,
params={"output": "base64"},
headers={"Authorization": f"Bearer {KEY}"},
json={
"html": invoice_html(order),
"format": "A4",
"margin": {"top": "20mm", "bottom": "20mm", "left": "15mm", "right": "15mm"},
"filename": f"invoice-{order['nomor']}.pdf",
},
timeout=30,
)
body = res.json()
if not body.get("is_success"):
raise RuntimeError(f"HTTP {res.status_code}: {body.get('message')}")
data = body["data"]
with open(data["filename"], "wb") as f:
f.write(base64.b64decode(data["base64"]))
return data["filename"], data["size"]
if __name__ == "__main__":
nama, ukuran = buat_invoice_pdf({
"nomor": "INV-2026-0042",
"pembeli": "PT Pelanggan Fiktif",
"item": [
{"nama": "Kaos polos hitam L", "qty": 3, "harga": 85000},
{"nama": "Topi kanvas", "qty": 2, "harga": 60000},
{"nama": "Ongkos kirim", "qty": 1, "harga": 18000},
],
})
print(f"Tersimpan: {nama} ({ukuran} byte)")
Seperti apa contoh respons API HTML ke PDF?
Dengan output=base64, respons memakai amplop standar APIPedia { is_success, message, data }. Berikut respons nyata dari template invoice di atas, dengan isi base64 dipotong:
{
"is_success": true,
"message": "PDF berhasil dibuat",
"data": {
"filename": "invoice-INV-2026-0042.pdf",
"size": 20304,
"maxPages": 100,
"base64": "JVBERi0xLjMKJf////8KOCAwIG9iago8PAov..."
}
}
| Field | Arti |
|---|---|
filename | Nama berkas sesuai field filename yang Anda kirim. |
size | Ukuran PDF dalam byte. Invoice satu halaman di contoh ini berukuran 20.304 byte. |
maxPages | Batas halaman per render, yaitu 100. Halaman di luar batas dipotong. |
base64 | Isi PDF. Decode menjadi byte sebelum disimpan atau dikirim. |
Tanpa output=base64, tidak ada amplop JSON pada respons sukses. Isi respons langsung berupa byte PDF, jadi cek status HTTP sebelum menulisnya ke berkas.
Bagaimana menambahkan nomor halaman di footer?
Set displayHeaderFooter ke true, lalu tulis {page} dan {pages} di footerTemplate. Kedua penanda itu diganti otomatis dengan nomor halaman saat ini dan jumlah halaman. Ini berguna untuk invoice dengan banyak baris atau laporan bulanan.
{
"html": "<h1>Laporan Penjualan Oktober 2026</h1>...",
"format": "A4",
"margin": { "top": "25mm", "bottom": "20mm", "left": "15mm", "right": "15mm" },
"displayHeaderFooter": true,
"headerTemplate": "Toko Contoh Sejahtera",
"footerTemplate": "Halaman {page} dari {pages}"
}
Beri margin atas dan bawah yang cukup agar header dan footer tidak menimpa isi dokumen.
Kesalahan umum apa yang perlu dihindari?
Sebagian besar masalah berasal dari template yang dirancang untuk browser, bukan dari API-nya. Periksa daftar berikut sebelum membuka tiket dukungan.
- Tata letak bergeser. Template memakai flexbox atau CSS Grid. Ganti dengan tabel.
- PDF tanpa gaya. CSS dimuat lewat
<link rel="stylesheet">. Pindahkan aturannya ke blok<style>. - PDF kosong dari
url. Halaman itu digambar oleh JavaScript, sedangkan JavaScript tidak dijalankan. Kirim HTML jadi lewat fieldhtml. - Logo hilang. Gambar berformat SVG atau WebP, atau berada di alamat internal. Pakai JPEG atau PNG di https publik.
- Mengirim
htmldanurlsekaligus. Isi tepat satu. - Mengandalkan
javascript,scale, atauprintBackground. Ketiga field ini diabaikan. - Render ulang setiap kali diunduh. Simpan PDF sekali di storage Anda, lalu sajikan dari sana.
- Data pengguna tidak di-escape. Nama atau catatan pembeli bisa merusak HTML. Selalu escape sebelum disisipkan.
Kode status yang perlu ditangani
| Status | Arti | Tindakan |
|---|---|---|
| 400 | Input tidak valid, URL privat, atau halaman gagal diambil | Baca message, yang menyebut field bermasalah |
| 401 | Token tidak ada, tidak dikenal, atau kedaluwarsa | Periksa header Authorization |
| 403 | Kunci tidak punya scope untuk endpoint ini | Buat kunci dengan scope yang sesuai |
| 503 | Mesin PDF sibuk, antrean penuh | Ulangi setelah nilai header Retry-After |
| 504 | Render melebihi 20 detik | Kecilkan dokumen atau kurangi gambar |
Pertanyaan yang sering diajukan
Apakah API HTML ke PDF APIPedia gratis?
Produk HTML to PDF tercatat di tier Free pada katalog APIPedia. Anda cukup membuat kunci di menu Token API pada konsol untuk mulai mencoba. Rincian kuota tiap paket ada di halaman paket konsol.
Apakah teks di PDF bisa diseleksi dan dicari?
Bisa. PDF dibangun dari teks, bukan dari tangkapan layar halaman. Nomor invoice, nama pelanggan, dan nominal tetap bisa disalin dan dicari.
Kenapa hasil PDF berbeda dari tampilan di browser?
Mesin render ini bukan browser. Ia membaca style inline dan blok <style> untuk properti umum seperti warna, border, font, margin, perataan, lebar, dan tinggi. Tata letak rumit tetap dirender, tetapi bisa bergeser dari tampilan aslinya.
Berapa ukuran maksimal dokumen yang bisa dirender?
HTML maksimal 2 MB, hasil maksimal 100 halaman dan 20 MB, dengan waktu render 20 detik. Untuk laporan yang lebih panjang, pecah menjadi beberapa PDF per periode.
Bisakah asisten AI membuat PDF lewat API ini?
Bisa. Server MCP APIPedia menyediakan tool html_to_pdf yang memanggil endpoint yang sama. Cara menyambungkannya ada di panduan menghubungkan APIPedia ke Claude lewat MCP.
Langkah berikutnya: coba di konsol
Invoice otomatis cukup butuh satu template HTML berbasis tabel dan satu request POST /v1/pdf/html. Buat kunci di console.apipedia.id, jalankan contoh curl di atas dengan invoice.html, lalu pindahkan logikanya ke backend Anda.
Untuk melengkapi alur toko online, isi baris ongkos kirim dengan estimasi dari API cek ongkir, dan hitung tanggal jatuh tempo pada hari kerja dengan API hari libur nasional.
Topik: #html ke pdf #invoice pdf #api dokumen #tutorial #nodejs #python
${c.about}
Tim APIPedia — Tim Engineering APIPedia
Tim engineering di balik APIPedia yang membangun dan memelihara API data Indonesia - ongkir, OCR KTP, data wilayah, BMKG, hingga server MCP - dan menulis panduan integrasinya dari pengalaman langsung.