Paperbase Docs

Call the API from a Next.js server route

Use raw fetch to generate a PDF from a Next.js App Router route handler.

This example shows how to call the Paperbase API directly with fetch from a Next.js 15 Route Handler — no SDK required.

Setup

Add your API key to .env.local:

.env.local
PAPERBASE_API_KEY=pb_live_…

Route handler

app/api/reports/route.ts
import { NextResponse } from "next/server";
 
export async function POST(request: Request) {
  const body = await request.json();
 
  const res = await fetch("https://api.paperbase.dev/v1/pdf/generate", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.PAPERBASE_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      input: {
        type: "markdown",
        content: body.markdown,
      },
      template: "report",
      theme: body.theme ?? {},
    }),
  });
 
  if (!res.ok) {
    const { error } = await res.json();
    return NextResponse.json(
      { error: error.human_message },
      { status: res.status },
    );
  }
 
  const result = await res.json();
 
  return NextResponse.json({
    url: result.url,
    preview_url: result.preview_url,
    page_count: result.page_count,
    warnings: result.warnings,
  });
}

Calling the route from a client component

app/dashboard/page.tsx
"use client";
 
async function generateReport(markdown: string) {
  const res = await fetch("/api/reports", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      markdown,
      theme: { accent_color: "#6366f1" },
    }),
  });
 
  if (!res.ok) {
    const { error } = await res.json();
    throw new Error(error);
  }
 
  return res.json(); // { url, preview_url, page_count, warnings }
}

Handling quota errors

if (res.status === 429) {
  const { error } = await res.json();
  if (error.code === "PB_MONTHLY_QUOTA_EXCEEDED") {
    // Surface to user — do not retry automatically
    return NextResponse.json({ error: error.human_message }, { status: 429 });
  }
  // PB_RATE_LIMIT_EXCEEDED — respect Retry-After
  const retryAfter = res.headers.get("Retry-After") ?? "5";
  await new Promise(r => setTimeout(r, parseInt(retryAfter, 10) * 1000));
  // retry the fetch once
}

Using idempotency keys

If your route handler can be retried (e.g. from a queue), pass an Idempotency-Key header to ensure only one PDF is generated per logical operation:

headers: {
  "Authorization": `Bearer ${process.env.PAPERBASE_API_KEY}`,
  "Content-Type": "application/json",
  "Idempotency-Key": `report-${userId}-${reportId}`,
},

Paperbase returns the cached response on a duplicate key with X-Idempotent-Replayed: true.


On this page