Skip to main content
Build a command-line tool that turns any topic into a polished, source-linked interactive data-story draft with Perplexity’s Agent API and perplexity/kimi-k3. Kimi K3 researches the topic with live web_search, reasons across the evidence, and hand-codes a self-contained HTML page with animated statistics, inline SVG charts, narrative, and citations. The CLI makes the long-running job durable, validates the page, and inserts source URLs from structured API results rather than trusting URLs written by the model.

Features

  • One submitted Agent API job handles research, synthesis, data visualization, and front-end implementation
  • Live web_search grounds the draft in current source material
  • Kimi K3 builds all HTML, CSS, JavaScript, and SVG without a chart library
  • Durable background mode saves the response ID immediately and polls the same run under a real local deadline without resubmitting the paid request
  • quick and showcase profiles balance latency, cost, and polish
  • Every statistic and plotted value must reference a numeric API search-result ID before the artifact can be saved
  • Canonical titles, dates, and URLs are injected from API metadata
  • Self-contained output works offline and receives a restrictive Content Security Policy
  • A sanitized JSON receipt records request settings, model, response ID, searches, sources, usage, cumulative client latency, per-attempt history, and provider-reported cost
  • The development test suite exercises the pinned SDK serializer and recovery path without network calls; this standalone page embeds that exact verified script
This pattern combines two capabilities that are usually separate: Kimi K3 provides long-horizon research-to-code execution, while Perplexity provides live search, tool orchestration, durable state, provenance metadata, and cost reporting. The result is an inspectable HTML artifact rather than a black-box answer.

Prerequisites

  • Python 3.10 or newer (tested with Python 3.12)
  • A Perplexity API key
  • Internet access for live Agent API runs

Installation

This is a self-contained tutorial. Copy the complete Python block under Full Code into a local file named data_story.py, then create an isolated environment and install the exact SDK version used for verification:
Python 3.10 or newer is required. The pinned dependency keeps request serialization and background-response behavior reproducible.

API Key Setup

Set the key only in your environment; do not paste it into the script or commit it to source control.
The Perplexity SDK reads PERPLEXITY_API_KEY automatically.

Quick Start

After saving the Full Code block as data_story.py, create a story with the default quick profile:
Create a higher-budget showcase draft at an explicit path:
The generated HTML is a source-linked draft, not an automatic publication system. Review every claim against its cited source before publishing.

Usage

Inspect the exact JSON-compatible request without calling the API:
If a run outlives the local process, resume the same response instead of creating another paid run:
Keep the same --receipt path when resuming to preserve the original request, archive earlier client errors with their attempt, and append resume history. The receipt distinguishes total client time from the most recent attempt and, for a newly submitted run, records the time spent reaching provider completion. The default receipt path is <output>.receipt.json.

Configuration Reference

Override any profile value directly:
max_output_tokens is an explicit ceiling, not a required reservation or a prediction of actual output. Billing follows the work the run actually performs; consult current pricing before high-budget runs. All documented K3 effort values are preserved. In perplexityai==0.43.1, the generated type omits max, so the script sends only that value through the SDK’s supported extra_body pass-through. Other effort levels use the typed reasoning field.

Dry Run Request Preview

--dry-run is deterministic and does not use an API key or spend credits. An abridged request preview looks like this:
A live completed run writes two files:
The terminal reports the saved size, source-link count, and a formatted provider-reported cost. Its phrase verified source links means that each used source ID and URL matched structured API results; it does not mean the cited claim was fact-checked. The receipt retains the raw provider value. If the provider omits cost data, the CLI reports unavailable rather than displaying a false $0.0000. client_elapsed_seconds is cumulative across recorded attempts; last_client_attempt_elapsed_seconds identifies a fast resume/finalization pass without misrepresenting it as full generation latency.

Example Output (truncated)

On a successful run, terminal output has this shape. This is an illustrative format example; query count, latency, linked sources, and cost vary by topic and run.

Observed Live Execution

The exact script embedded below was exercised against the live Agent API on August 7, 2026 UTC (August 6 in US Pacific time). One paid background POST was submitted, automatic create retries were disabled, and the same response ID was polled to completion. The structural and browser checks passed: four statistic cards, three SVG charts, seven focusable data marks, canonical source injection, no external assets or background requests, no console warnings or errors, no horizontal overflow at 1,440px or 390px, and no API-key leakage. The generated page still failed publication-level editorial review. Its date chip omitted earlier plotted years, one chart presented a period average as a late-period endpoint, one sentence claimed that every major US lab shipped open weights, one sentence changed a download-share finding into a model-count claim, and the generated wrapper produced two visible Sources headings. These findings are intentionally reported here: API completion and structural grounding produce an inspectable draft, not an automatic fact-check or publication guarantee.
This record proves that the Agent API request completed and the CLI saved an artifact. It does not certify factual accuracy, chart interpretation, accessibility, or publication readiness.
Latency, search work, token use, and cost vary by topic and run. This observed receipt is evidence that the workflow executed successfully, not a benchmark or pricing promise.

Code Walkthrough

  1. The CLI builds one Agent API request with web_search, a K3 effort level, an output ceiling, and a maximum number of agent steps.
  2. It submits once with background=True and store=True, with automatic retries disabled for the create call.
  3. The background create response returns an ID immediately; the CLI writes it to the receipt before doing anything else.
  4. The CLI polls client.responses.retrieve(response_id) with bounded request timeouts and explicit backoff. Status changes and newly visible search queries are printed without holding a long-lived stream open.
  5. K3 labels every statistic card and SVG data mark with a numeric result ID and leaves one PERPLEXITY_SOURCES placeholder.
  6. On completion, the CLI validates the document, rejects clipped chart marks and reduced-motion-unsafe SVG animation, matches every used ID against response.output search results, injects authoritative source links and a Content Security Policy, then writes the HTML atomically.
The only success status is completed. Pending statuses are queued and in_progress; every other status is treated as a terminal non-success so schema drift cannot create an infinite polling loop. If the create connection fails before any response ID arrives, the submission outcome is ambiguous. The CLI records submission_unknown and deliberately does not retry. Check your API activity before deciding whether to submit again. K3 never needs to reproduce external URLs. The prompt requires citation fragments such as #source-3 and data-source-id="3"; the CLI inserts the URL for result 3 from the API’s structured search_results output. The validator rejects incomplete documents, unknown source IDs, model-written external URLs, remote assets, frames, active forms, network-capable JavaScript, conflicting source mappings, non-focusable chart marks, SVG SMIL animation, and simple mark geometry outside a chart’s viewBox. It also rejects global svg { width: 100% } rules that can accidentally turn small interface icons into page-sized graphics. This proves structural source linkage and catches common rendering failures, not semantic entailment or every possible scale error: a human must still verify that the prose and chart geometry correctly interpret each source.

Prompting Guidance

  • Keep research and artifact construction in one explicit two-phase prompt so K3 can connect source IDs to the HTML it writes.
  • Ask for exact source terminology and prohibit inferred scope or rhetorical comparisons. A citation can exist while the surrounding claim still overstates what the source establishes.
  • Specify the chart contract mechanically: numeric data-source-id, matching citation, focusable simple geometry, a shared axis scale, visible bounds, and a tooltip for every mark.
  • Require CSS-only animation plus a reduced-motion media query. SVG SMIL animation is deliberately rejected because CSS cannot reliably disable it.
  • Scope responsive sizing to figure svg; never apply chart dimensions to every SVG on the page.
  • Treat output as a draft. The validator checks structure, provenance wiring, and common rendering failures; editorial and numeric review remain required.

Full Code

Save this complete block as data_story.py. It is byte-for-byte identical to the implementation used for the offline verification and live paid run reported above.

Offline Verification

After saving the Full Code block as data_story.py, these checks make no network request and spend no API credits:
The development test suite is not duplicated in this standalone page. Before publication, the exact embedded script passed 23 offline unit tests with warnings treated as errors under Python 3.12.13, plus a Python 3.10 grammar parse. Those tests cover exact high and max SDK serialization, one-POST background execution, timeouts, terminal states, --resume, cost handling, source injection, CSP insertion, chart bounds, reduced motion, unsafe markup, receipt accounting, secret redaction, and README/source synchronization.

Limitations

  • The generated page is a draft. Structural citation checks cannot determine whether a sentence misreads, overstates, or omits context from its source.
  • Simple SVG marks are checked against the viewBox, but a coordinate can still be internally inconsistent with an axis while remaining in bounds. Verify the plotted scale and every label before publication.
  • Verify SVG label size at mobile widths. A chart can avoid horizontal overflow and still make text too small to read.
  • Search quality depends on current coverage. Sparse or conflicting results may cause validation to fail, in which case no HTML is published.
  • High-effort research and full-page generation can take many minutes. Use the response ID and --resume; do not blindly submit duplicates.
  • The output contract is one self-contained HTML page. Multi-page projects need a different artifact workflow.
  • A browser-level visual audit is still recommended before publishing to check layout, tooltips, accessibility, and mobile behavior.
  • Run receipts never contain the API key, but the topic and source snippets may still be sensitive and should be reviewed before sharing.

Resources