AnswerLine Start free

, Tutorials · Perplexity

Monitor Perplexity answers with an API: sources and citations

Monitoring Perplexity means running a fixed prompt set against Perplexity on a schedule, per market, and storing the answer, its numbered sources and the follow-up searches it suggests, as structured data you can compare between runs. The response lists sources in order and maps each inline citation back to them, so citation position can be measured directly.

Definitions

The endpoint and request fields

POST https://api.answerline.dev/v1/monitor/perplexity, with Authorization: Bearer <key> (authentication).

Field Required Notes
prompt yes The prompt sent to Perplexity (up to 2,000 characters).
country yes Any country code; GET /v1/countries?model=perplexity lists supported values.
state no Not accepted: Perplexity answers at country level, and a request with state is refused with a 400.
include.markdown no Adds result.markdown.
include.html no Adds result.html, a link to the answer’s public page on perplexity.ai.
include.rawResponse no Adds result.rawResponse, the answer as normalized stream events; include.rawWire returns null on Perplexity.

Step 1: send a prompt

curl

curl -X POST https://api.answerline.dev/v1/monitor/perplexity \
  -H "Authorization: Bearer $ANSWERLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "Which project management tools do agencies use?",
    "country": "GB",
    "include": { "markdown": true }
  }'

Python (requests)

import os
import requests

API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['ANSWERLINE_API_KEY']}"}

resp = requests.post(
    f"{API}/v1/monitor/perplexity",
    headers=HEADERS,
    json={"prompt": "Which project management tools do agencies use?", "country": "GB"},
    timeout=360,
)
resp.raise_for_status()
result = resp.json()["result"]

for s in result.get("sources", []):
    print(s["position"], s["url"])
print("suggested:", result.get("related_queries", []))

TypeScript (@answerline/sdk)

import { Client } from "@answerline/sdk";

interface PerplexityResult {
  text?: string;
  markdown?: string;
  sources?: { position: number; url: string; label?: string; description?: string }[];
  citationPills?: { citationPillId: number; position: number; url: string; domain: string; label: string }[];
  related_queries?: string[];
}

const client = new Client({ apiKey: process.env.ANSWERLINE_API_KEY! });
const { result } = await client.monitor.perplexity<PerplexityResult>({
  prompt: "Which project management tools do agencies use?",
  country: "GB",
});
console.log((result.sources ?? []).map((s) => s.url));

The TypeScript SDK is on npm as @answerline/sdk; the Python examples call the HTTP API with requests.

Step 2: the response fields

The body is { "success": true, "result": { ... } }.

Field Meaning
text The answer as plain text.
markdown The answer in Markdown, with include.markdown.
sources[] Every source, in order: position, url, label, description.
citationPills[] One entry per citation-group-and-source pair: citationPillId (1-based), position (1-based index into sources), url, domain, label, optional description. Omitted when there are none.
related_queries[] Follow-up searches Perplexity suggests.
rawResponse[] The answer as normalized stream events, with include.rawResponse. result.rawWire (with include.rawWire) is null on Perplexity.

Citation groups

Perplexity cites sources in bracketed groups such as [1][2][3]. The API returns such a group as three citationPills entries with the same citationPillId, each carrying its own url, domain and label. label is always present but may be empty; use domain or url to identify the source.

from collections import defaultdict

def citation_groups(result: dict) -> dict[int, list[int]]:
    groups = defaultdict(list)
    for pill in result.get("citationPills", []):
        groups[pill["citationPillId"]].append(pill["position"])
    return dict(groups)

A source cited in many groups carries more of the answer than one cited once. Count groups per domain as well as positions.

Step 3: metrics from a run

from urllib.parse import urlsplit

def on_domain(url: str, domain: str) -> bool:
    host = (urlsplit(url).hostname or "").lower()
    return host == domain or host.endswith("." + domain)

def perplexity_metrics(result: dict, brand: str, domain: str) -> dict:
    positions = [s["position"] for s in result.get("sources", []) if on_domain(s["url"], domain)]
    groups = {p["citationPillId"] for p in result.get("citationPills", []) if on_domain(p["url"], domain)}
    return {
        "cited": bool(positions),
        "best_position": min(positions) if positions else None,
        "groups_citing": len(groups),
        "mentioned": brand.lower() in result.get("text", "").lower(),
        "suggested": result.get("related_queries", []),
    }

Roll these up per prompt, market and run:

  1. Citation share: share of answers with cited.
  2. Average best position among cited answers. Lower is earlier in the list.
  3. Group coverage: average groups_citing, a measure of how much of the answer relies on your pages.
  4. Mention rate: share of answers naming the brand, cited or not (mentions vs citations).

Then collect related_queries across the prompt set. They are suggestions shown to the user and good material for prompt set design and keyword research.

Step 4: run it at volume

A synchronous call waits for the full answer. For a prompt set, such as daily brand checks or a category watchlist, submit async tasks with a webhook and let results arrive as each completes:

[
  {
    "taskType": "PERPLEXITY",
    "payload": { "prompt": "Which project management tools do agencies use?", "country": "US" },
    "idempotencyKey": "pplx-pm-tools-agencies-US-2026-09-15",
    "webhook": { "url": "https://you.example/hook" }
  },
  {
    "taskType": "PERPLEXITY",
    "payload": { "prompt": "Which project management tools do agencies use?", "country": "GB" },
    "idempotencyKey": "pplx-pm-tools-agencies-GB-2026-09-15",
    "webhook": { "url": "https://you.example/hook" }
  }
]

POST that array to /v1/async/task/batch (1 to 500 tasks). With the SDK:

const markets = ["US", "GB"];
const tasks = markets.map((country) => ({
  taskType: "PERPLEXITY" as const,
  payload: { prompt: "Which project management tools do agencies use?", country },
  idempotencyKey: `pplx-pm-tools-agencies-${country}-2026-09-15`,
  webhook: { url: "https://you.example/hook" },
}));
const batch = await client.createBatch(tasks);
console.log(batch.summary); // { total, succeeded, failed }

Each item in results answers by index; failed items carry VALIDATION_ERROR, RESOURCE_ALREADY_EXISTS or INSUFFICIENT_CREDITS without affecting the rest. The webhook body is the GET /v1/async/task/{id} JSON, with the Perplexity result under response.result. Verify the signature (verify webhook signatures) and dedupe by task.id. For the batch-and-webhook plumbing, see the rank-tracking pipeline; for key design, idempotency keys explained.

What it costs

Per billing, at the Starter tier:

Request Price
Perplexity request $0.0015
include.markdown or include.rawResponse +0

Worked example: 40 prompts in 3 markets, weekly, is 40 x 3 x $0.0015 = $0.18 per run as tasks, or $0.72 for four weekly runs. A new account’s 100 free requests work on every engine. See pricing and cost planning.

Sync or async

Details in sync calls, async tasks or webhooks.

Pitfalls

Perplexity in a cross-engine program

Citation share on Perplexity is most useful next to the same number on other engines, such as Gemini and ChatGPT. Where you are cited on one and missing on another, compare the sources of both to see who takes the slot. How AI engines choose citations and running your own citation study go deeper; see also the AI visibility tracking use case.

See every field on the Perplexity engine page and in the API reference.

Questions

Which fields does a Perplexity result carry?

text, markdown (with include.markdown), sources, citationPills and related_queries. citationPills is omitted when the answer has none, so read it with a default.

What are related_queries?

The follow-up searches Perplexity suggests to the user. They are good material for prompt set design and keyword research.

How do I rebuild Perplexity's bracketed citation groups?

Group result.citationPills by citationPillId. A group such as [1][2][3] appears as three entries sharing one citationPillId, each with its own url, domain and label, and position pointing into result.sources.

Why do some Perplexity field names use snake_case?

The Perplexity response keeps Perplexity-specific names such as related_queries, while shared fields such as citationPills and rawResponse use camelCase. Use the names exactly as documented.

Can I monitor Perplexity in a specific US state?

No. Perplexity answers at country level: a request that sends state is refused with a 400 and not billed.

Try it on your own prompts

100 free requests on any engine, no card. One POST returns the answer, sources and citations as JSON.

Keep reading