How to Summarise Zendesk with LlamaIndex

How to extract structured Zendesk article data into a LlamaIndex pipeline with EnConvert Distill, returning schema-matched fields in one synchronous call

Get API key

A developer building a LlamaIndex pipeline over a Zendesk help centre needs structured article data, including title, body, category, and tags, before passing it into a retrieval layer or agent. Zendesk help centre pages mix article content with navigation, sidebars, and boilerplate, and scraping them manually means maintaining CSS selectors that break when Zendesk updates its theme. Passing raw page HTML into LlamaIndex keeps that boilerplate in the context and reduces retrieval precision. EnConvert’s Distill endpoint accepts up to 50 Zendesk article URLs with a schema defining the fields you need and returns a results array with each URL’s extracted data normalized to exactly that schema in one synchronous call, with no HTML parsing or preprocessing required.

Example

python
import os

import requests
from llama_index.core import Document, VectorStoreIndex

# Step 1: Define the schema and target Zendesk article URLs
schema = {
    "title": "the article title",
    "body": "the full article body text, without navigation or sidebars",
    "category": "the help centre category the article belongs to",
    "tags": "a comma-separated list of article tags",
}

# Step 2: Call Distill — synchronous, no job_id, no polling, no download step
# Distill requires a paid plan; the free tier returns 402
response = requests.post(
    "https://api.enconvert.com/v2/distill",
    headers={
        "X-API-Key": os.environ["ENCONVERT_API_KEY"],
        "Content-Type": "application/json",
    },
    json={
        "urls": [url.strip() for url in os.environ["ZENDESK_ARTICLE_URLS"].split(",")],  # public Zendesk Help Center article URLs
        "schema": schema,
    },
    timeout=120,  # increase for larger batches
)
if response.status_code == 402:
    raise RuntimeError("Distill requires a paid plan")
response.raise_for_status()

# Step 3: Parse results — failed pages arrive as rows, not as request errors
documents = []
for result in response.json()["results"]:
    if result["status"] != "completed":
        print(f"Skipped {result['url']}: {result['status']}")
        continue
    if result.get("render_quality", 1.0) < 0.5:
        # Low render_quality flags anti-bot challenges or login walls
        print(f"Low render quality for {result['url']}: {result['render_quality']}")
        continue

    data = result.get("data") or {}  # keys match the schema exactly: title, body, category, tags
    documents.append(
        Document(
            text=data.get("body"),
            metadata={
                "title": data.get("title"),
                "category": data.get("category"),
                "tags": data.get("tags"),
                "source_url": result["url"],
            },
            id_=result["url"],
        )
    )

if not documents:
    raise RuntimeError("No Zendesk articles were extracted successfully")

print(f"Built {len(documents)} LlamaIndex documents")

# Step 4: Load into a VectorStoreIndex and query
# LlamaIndex's default embedding model and LLM read OPENAI_API_KEY from the environment
index = VectorStoreIndex.from_documents(documents)
query_engine = index.as_query_engine(similarity_top_k=3)

answer = query_engine.query("How does a user reset their password?")
print(answer)

What you get back

The Distill endpoint returns a results array with one entry per URL, each containing a data object normalized to exactly the schema’s keys — title, body, category, and tags — regardless of the page’s surrounding navigation or Zendesk theme. Scraping Zendesk help centre pages manually requires maintaining CSS selectors that break when Zendesk updates its theme, and passing raw page HTML into LlamaIndex inflates context with navigation and boilerplate that reduces retrieval precision.


Distill runs on paid plans only, and the free tier returns a 402 for this endpoint, so the free key covers EnConvert’s other endpoints.

Get free API key — 500 ops per month, no card required.