AnswerLine Start free

Perplexity

Perplexity sources

Perplexity's numbered citations in display order, with the inline groups they appear in.

Perplexity cites every answer. `sources` returns the cited pages in display order with URL, label and description, and `citationPills` records which sources share each bracketed inline citation.

Returned in result

Field
sources
citationPills

Request

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

What Perplexity’s sources are

A Perplexity source is a page the answer lists. Perplexity numbers its sources and places citation chips next to the sentences they support. This API returns both layers: sources is the ordered list, and citationPills records which sources sit behind each chip. The chips are removed from text and markdown. sources can include a page Perplexity lists but no chip cites; the response does not mark which.

Every sentence a reader checks leads back to that list, so it is the most direct measure of whether Perplexity relies on your pages. From it you get citation share (of answers and of inline citations, yours versus competitors), position (whether you are source 1 or source 8) and co-citation (which domains share a chip with yours, since a chip groups sources that support the same claim).

The Perplexity monitoring walkthrough covers the whole response; this page covers citations.

Field reference

result.sources[]

Field Type Meaning
position number The source’s number in Perplexity’s citation order, 1-based.
url string Page the answer cites.
label string Title shown in the sources list.
description string The summary line beside that title.

result.citationPills[]

Field Type Meaning
citationPillId integer, required The chip the row came from, numbered from 1.
position integer, required 1-based index into result.sources; the safer key for joining the two.
url string, required The page behind the chip.
domain string, required Host from url, for grouping by brand.
label string, required Title from the sources list. Always present, sometimes "", so identify by domain or url.
description string The source’s summary line. Omitted when absent.

A chip citing three sources becomes three rows sharing a citationPillId, each with its own label, url and domain. The field is absent when the answer has no chips.

Annotated excerpt

Field names and types below are those of the POST /v1/monitor/perplexity result schema; the content is invented.

{
  "success": true,
  "result": {
    "text": "Most small agencies track time inside their project tool, while larger ones...",
    "sources": [
      {
        "position": 1,
        "url": "https://www.example-reviews.com/agency-time-tracking",
        "label": "Time tracking for agencies",
        "description": "Compared on billing exports and client reports..."
      },
      {
        "position": 2,
        "url": "https://forum.example.org/t/agency-time-tracking",
        "label": "What does your agency use for time tracking?",
        "description": "Discussion thread..."
      },
      {
        "position": 3,
        "url": "https://docs.example-timer.com/billing",
        "label": "Billing rates",
        "description": "Set rates per client and project..."
      }
    ],
    "citationPills": [
      // One chip citing sources 1 and 2: two entries, same citationPillId (1-based).
      { "label": "Time tracking for agencies", "citationPillId": 1, "url": "https://www.example-reviews.com/agency-time-tracking", "domain": "example-reviews.com", "position": 1 },
      { "label": "What does your agency use for time tracking?", "citationPillId": 1, "url": "https://forum.example.org/t/agency-time-tracking", "domain": "forum.example.org", "position": 2 },
      // A label can be empty: identify the source by domain or url.
      { "label": "", "citationPillId": 2, "url": "https://docs.example-timer.com/billing", "domain": "docs.example-timer.com", "position": 3 }
    ]
  }
}

Cost and include flag

No include flag is needed and there are no add-ons. Every Perplexity request costs $0.0015, whether or not you set include.markdown, include.html, include.rawResponse or include.rawWire. A citation study of 20 prompts, 5 samples each, is 100 tasks: $0.15.

Example: citation share by domain

The script samples each prompt, then computes per domain the fraction of answers citing it at least once, its share of all citationPills entries, and the median position of its first source in answers that cite it. It also lists the domains that most often share a chip with yours.

import os
import time
from collections import Counter, defaultdict
from datetime import date
from statistics import median
from urllib.parse import urlparse

import requests

API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
PROMPTS = ["best time tracking app for agencies", "how do agencies bill clients for hours"]
SAMPLES = 5
DAY = date.today().isoformat()
MY_DOMAIN = "example-timer.com"


def run_tasks(tasks):
    resp = requests.post(f"{API}/v1/async/task/batch", headers=HEADERS, json=tasks, timeout=60)
    resp.raise_for_status()
    pending = {item["task"]["id"] for item in resp.json()["results"] if item["success"]}
    results = []
    while pending:
        time.sleep(10)
        for task_id in list(pending):
            body = requests.get(f"{API}/v1/async/task/{task_id}", headers=HEADERS, timeout=30).json()
            status = body["task"]["status"]
            if status in ("COMPLETED", "FAILED"):
                pending.discard(task_id)
                if status == "COMPLETED":
                    results.append(body["response"]["result"])
    return results


def site(url_or_host):
    host = urlparse(url_or_host).hostname if "://" in url_or_host else url_or_host
    parts = (host or "").split(".")
    return ".".join(parts[-2:])  # registrable domain for simple TLDs only


tasks = [
    {
        "taskType": "PERPLEXITY",
        "payload": {"prompt": prompt, "country": "US"},
        "idempotencyKey": f"timetrack-sources-{p}-s{s}-{DAY}",
    }
    for p, prompt in enumerate(PROMPTS)
    for s in range(SAMPLES)
]
answers = run_tasks(tasks)

answer_hits = Counter()
inline = Counter()
first_position = defaultdict(list)
co_cited = Counter()
for result in answers:
    first = {}
    for src in sorted(result.get("sources", []), key=lambda s: s["position"]):
        first.setdefault(site(src["url"]), src["position"])
    answer_hits.update(first.keys())
    for domain, pos in first.items():
        first_position[domain].append(pos)

    chips = defaultdict(set)
    for pill in result.get("citationPills", []):
        inline[site(pill["domain"])] += 1
        chips[pill["citationPillId"]].add(site(pill["domain"]))
    for domains in chips.values():
        if MY_DOMAIN in domains:
            co_cited.update(domains - {MY_DOMAIN})

total_inline = sum(inline.values()) or 1
print(f"{len(answers)} answers")
for domain, hits in answer_hits.most_common(15):
    print(f"{domain:30} answers {hits / len(answers):5.0%}   inline {inline[domain] / total_inline:5.0%}   "
          f"median position {median(first_position[domain]):.0f}")
print("co-cited with", MY_DOMAIN, co_cited.most_common(5))

site() keeps the last two labels of the host, which rolls docs. and www. subdomains into one brand but is wrong for suffixes such as co.uk; use a public-suffix library for reporting. Save raw results so you can regroup later without new runs.

Pitfalls

Counting citationPills entries counts sources cited inline. To count visible chips, count distinct citationPillId values per answer.

Perplexity’s citationPillId starts at 1 and ChatGPT’s at 0. Join to sources on position, which is 1-based on both.

Perplexity sources have no footnote or datePublished, so freshness analysis needs the page itself.

citationPills is omitted when there are none, though the answer can still have sources. Decide whether such answers count toward inline share (they add zero) and keep the rule. label can be "", so group and display by domain.

Sources and their order change between runs; report rates with sample sizes. Sampling vs census covers the trade-off.

Questions

Do I need an include flag to get Perplexity sources?

No. sources and citationPills are part of the Perplexity result at the base price: $0.0015. Perplexity has no priced add-ons.

How is Perplexity citationPills different from sources?

sources lists the pages Perplexity gives for the answer, in its citation order. citationPills has one entry per chip and source pair for the inline citations, grouped by a 1-based citationPillId, with position pointing back into sources.

Does Perplexity return a publish date or footnote flag for sources?

No. Perplexity sources carry position, url, label and description. footnote and datePublished exist on ChatGPT sources, not on Perplexity.

Why is citationPills missing from some Perplexity results?

The field is omitted when the answer carries no inline pills. Read it with a default and treat a missing key as no pills.

How do I compare Perplexity citation IDs with ChatGPT?

Perplexity citationPillId starts at 1 and ChatGPT's starts at 0. position is 1-based on both and is the safer key for joining pills to sources.

More Perplexity data

All Perplexity fields · API reference