March 17, 2026  ·  6 min read

Generate PDF Reports from LlamaIndex Agents with DocAPI

LlamaIndex agents can query documents, reason over data, and synthesize answers — but producing a PDF requires extra plumbing. Here's how to wire DocAPI into a LlamaIndex FunctionTool so your agent can generate polished PDF reports autonomously.

LlamaIndex is built for retrieval — indexing documents, querying them, synthesizing answers across large corpora. When you layer an agent on top, you get something that can reason over your data, look things up, and produce detailed responses.

The gap: that response is text. When you need a deliverable — a PDF report, a formatted summary, something a client can open and share — you're back to manual work.

DocAPI closes that gap.

The problem with PDF generation in LlamaIndex pipelines

The obvious options — reportlab, weasyprint, pdfkit — technically work but introduce friction:

The cleaner pattern: let the agent generate HTML (which it does naturally), then call an API that renders it into a PDF. The agent focuses on content. DocAPI handles the rendering.

What DocAPI is

DocAPI is a PDF generation API built for AI agents. Give it HTML, get back a PDF.

What makes it different:

Setup

Install dependencies:

pip install llama-index llama-index-llms-openai requests

Register for an API key — no email required:

import requests

res = requests.post("https://docapi.co/api/register")
data = res.json()

print(data["api_key"])       # save this
print(data["usdc_address"])  # fund this to add more credits
print(data["free_calls"])    # 10 free calls to start

Set your keys:

export DOCAPI_KEY="dk_your_key_here"
export OPENAI_API_KEY="sk_your_key_here"

Create a LlamaIndex FunctionTool for DocAPI

LlamaIndex agents use FunctionTool to wrap callable functions. Here's the DocAPI tool:

import os
import requests
from llama_index.core.tools import FunctionTool

DOCAPI_KEY = os.environ["DOCAPI_KEY"]
DOCAPI_ENDPOINT = "https://docapi.co/api/pdf"


def generate_pdf(html_content: str) -> str:
    """
    Convert an HTML string to a PDF using DocAPI.
    Returns the URL of the generated PDF file.
    Use this when you need to produce a downloadable PDF report.
    The html_content should be a complete HTML document with inline CSS styling.
    """
    response = requests.post(
        DOCAPI_ENDPOINT,
        headers={
            "Authorization": f"Bearer {DOCAPI_KEY}",
            "Content-Type": "application/json",
        },
        json={"html": html_content},
    )
    response.raise_for_status()

    # Monitor remaining credits
    credits_remaining = response.headers.get("X-Credits-Remaining")
    if credits_remaining is not None:
        remaining = int(credits_remaining)
        if remaining < 10:
            print(f"[DocAPI] Warning: only {remaining} credits left.")
        else:
            print(f"[DocAPI] Credits remaining: {remaining}")

    return response.json()["url"]


pdf_tool = FunctionTool.from_defaults(fn=generate_pdf)

That's the whole tool. Pass it to any ReActAgent or FunctionCallingAgent.

Build a report agent

Here's a full working agent that generates a market report PDF:

import os
from llama_index.core.agent import ReActAgent
from llama_index.llms.openai import OpenAI

# Import our tool from above
from tools.docapi import pdf_tool

llm = OpenAI(model="gpt-4o", temperature=0)

agent = ReActAgent.from_tools(
    tools=[pdf_tool],
    llm=llm,
    verbose=True,
    system_prompt="""You are a financial analyst assistant that produces PDF reports.

When asked to generate a report:
1. Write a thorough analysis in well-structured HTML with inline CSS
2. Include: title section, executive summary, key metrics, recent context, risks, and outlook
3. Use clean styling: white background, dark text, readable fonts, clear hierarchy
4. Call generate_pdf with the complete HTML string
5. Return the PDF URL as your final response

Always produce a PDF — never return raw text as the final output.""",
)

if __name__ == "__main__":
    response = agent.chat(
        "Generate a market report PDF for Apple Inc. (AAPL) as of today."
    )
    print("\nPDF report:", response)

Run it:

python agent.py

The agent reasons through the task, writes the HTML report, calls generate_pdf, and returns a URL. No file system, no Puppeteer, no extra dependencies.

Combine with a query engine

LlamaIndex's real strength is retrieval. Here's how to build an agent that queries your documents and produces a PDF from the results:

import os
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.core.tools import QueryEngineTool, FunctionTool
from llama_index.core.agent import ReActAgent
from llama_index.llms.openai import OpenAI

from tools.docapi import generate_pdf

# Build an index over your documents
documents = SimpleDirectoryReader("./data").load_data()
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine()

# Wrap the query engine as a tool
query_tool = QueryEngineTool.from_defaults(
    query_engine=query_engine,
    name="document_search",
    description=(
        "Use this to search and retrieve information from the company's internal documents, "
        "reports, and financial data. Input should be a specific question."
    ),
)

pdf_tool = FunctionTool.from_defaults(fn=generate_pdf)

llm = OpenAI(model="gpt-4o", temperature=0)

agent = ReActAgent.from_tools(
    tools=[query_tool, pdf_tool],
    llm=llm,
    verbose=True,
    system_prompt=(
        "You are a financial analyst. Use document_search to find relevant data, "
        "then synthesize it into a polished HTML report and call generate_pdf. "
        "Return the PDF URL."
    ),
)

response = agent.chat(
    "Search our Q4 earnings documents and generate a PDF summary report."
)
print("PDF report:", response)

The agent uses document_search to pull relevant data, synthesizes a report, and generates the PDF — all in one invocation.

What the HTML looks like

Before calling the tool, the agent produces something like this:

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: -apple-system, Georgia, serif; max-width: 820px; margin: 48px auto; color: #111; line-height: 1.6; }
    h1 { font-size: 2rem; border-bottom: 3px solid #111; padding-bottom: 14px; }
    h2 { font-size: 1.2rem; color: #444; margin-top: 2.5rem; text-transform: uppercase; letter-spacing: 0.05em; }
    .metrics { display: flex; gap: 2rem; margin: 1.5rem 0; }
    .metric .label { font-size: 0.75rem; text-transform: uppercase; color: #888; }
    .metric .value { font-size: 1.6rem; font-weight: 700; }
  </style>
</head>
<body>
  <h1>Apple Inc. (AAPL) — Q4 Summary</h1>
  <p><strong>Report Date:</strong> March 17, 2026</p>

  <h2>Executive Summary</h2>
  <p>Apple's Q4 results reflect continued strength in services, with revenue up 12% YoY...</p>

  <h2>Key Metrics</h2>
  <div class="metrics">
    <div class="metric"><div class="label">Revenue</div><div class="value">$124B</div></div>
    <div class="metric"><div class="label">EPS</div><div class="value">$2.18</div></div>
    <div class="metric"><div class="label">Services Rev</div><div class="value">$26B</div></div>
  </div>

  <!-- risks, outlook, etc. -->
</body>
</html>

DocAPI renders it through Chrome and returns a PDF. The agent never touched the file system.

What's next

MCP integration. If you're running Claude Desktop, Cursor, or any MCP-compatible host alongside LlamaIndex, point it at mcp.docapi.co. The PDF tool shows up automatically — no wrapper code needed.

USDC autopay. DocAPI supports USDC payments on Base. With Coinbase's AgentKit, your agent can hold a wallet, check X-Credits-Remaining after each PDF call, and automatically top up when the balance gets low. Fully autonomous — no babysitting.


LlamaIndex is powerful for retrieval. DocAPI is the missing output layer that turns retrieved, synthesized content into something you can actually hand to a client.

Full docs at docapi.co.

Get posts like this every week

← All posts