{"id":20031,"date":"2026-09-28T00:19:15","date_gmt":"2026-09-28T04:19:15","guid":{"rendered":"https:\/\/wp.glbgpt.com\/?p=20031"},"modified":"2026-09-28T00:19:17","modified_gmt":"2026-09-28T04:19:17","slug":"perplexity-api-tutorial","status":"publish","type":"post","link":"https:\/\/wp.glbgpt.com\/id\/hub\/perplexity-api-tutorial","title":{"rendered":"Tutorial API Perplexity: Membuat Jawaban yang Di-ground ke Sumber Secara Bertahap"},"content":{"rendered":"<style>\n[class*=\"pat-\"]{box-sizing:border-box}\np.pat-p,ul.pat-list{font:17px\/1.8 system-ui,-apple-system,\"Segoe UI\",sans-serif;color:#243F46;margin:0 0 22px}\np.pat-eyebrow{font:750 12px\/1.5 system-ui,sans-serif;letter-spacing:.12em;color:#35756f;margin:0 0 22px}\nh2.pat-h2{font:750 30px\/1.28 system-ui,sans-serif;color:#173E48;border-top:1px solid #D5E1DB;padding-top:23px;margin:58px 0 22px;scroll-margin-top:24px}\nh3.pat-h3{font:700 22px\/1.4 system-ui,sans-serif;color:#245D56;margin:30px 0 14px}\n.pat-p a,.pat-list a,.pat-table a{color:#146B61;text-decoration:underline;text-underline-offset:3px}\n.pat-p code,.pat-list code,.pat-table code{font:.88em Consolas,monospace;overflow-wrap:anywhere;background:#EEF3EF;padding:2px 4px;border-radius:3px}\np.pat-quick{background:#F5EDDA;border-left:4px solid #B99749;padding:23px 26px}\np.pat-note{background:#EEF4EF;border-radius:10px;padding:20px 24px}\nul.pat-toc{background:#EEF4EF;padding:24px 24px 24px 46px;border-radius:10px;columns:2;column-gap:32px;font-size:15px}\n.pat-list li{margin:8px 0;break-inside:avoid}\nul.pat-checklist{background:#F7F2E8;border:1px solid #DFD1B2;border-radius:10px;padding:20px 24px 20px 46px}\nfigure.pat-table{display:block;max-width:100%;overflow-x:auto;border:1px solid #CEDDD5;border-radius:10px;margin:26px 0}\n.pat-table table{border-collapse:collapse;width:100%;min-width:610px;font:14px\/1.65 system-ui,sans-serif}\n.pat-table th{background:#215957;color:white;text-align:left;padding:14px 16px;font-weight:650}\n.pat-table td{padding:13px 16px;vertical-align:top;border-bottom:1px solid #DFE7DF}\n.pat-table tbody tr:nth-child(even){background:#F2F6F0}\n.pat-table td:first-child{font-weight:600}\npre.pat-code{max-width:100%;overflow-x:auto;white-space:pre;padding:24px;border-radius:10px;background:#142F3C;color:#ECF7EF;font:14px\/1.7 Consolas,monospace;margin:24px 0}\n.pat-code code{font:inherit;white-space:pre;overflow-wrap:normal}\nfigure.pat-image{margin:30px 0}.pat-image img{display:block;width:100%;height:auto;border-radius:12px}\n.pat-image figcaption{font:14px\/1.6 system-ui,sans-serif;color:#627773;margin-top:10px}\n@media(max-width:650px){p.pat-p,ul.pat-list{font-size:16px}h2.pat-h2{font-size:26px;margin-top:44px}h3.pat-h3{font-size:20px}ul.pat-toc{columns:1}pre.pat-code{padding:18px;font-size:13px}p.pat-quick{padding:18px}.pat-table th,.pat-table td{padding:11px 12px}}\n\n\/* Native image blocks: same presentation as the approved preview. *\/\nfigure.wp-block-image.pat-image{display:block;max-width:100%;margin:30px 0}\nfigure.wp-block-image.pat-official{width:100%;max-width:848px;margin:30px auto}\nfigure.pat-image>a{display:block}figure.pat-image img{display:block;width:100%;max-width:100%;height:auto;border-radius:12px}\nfigure.pat-image figcaption.wp-element-caption{font:14px\/1.6 system-ui,sans-serif;color:#627773;margin:10px 0 0;text-align:left}\n.pat-image figcaption a{color:#146B61;text-decoration:underline;text-underline-offset:3px}\n<\/style>\n\n\n\n<p class=\"pat-p pat-eyebrow wp-block-paragraph\">PERPLEXITY API \u00b7 PYTHON TUTORIAL \u00b7 2026<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">This Perplexity API tutorial shows you how to build a Python script that returns an answer with clickable web sources. Use the Agent API to send a question, read the answer, match its citation markers to source IDs, and export a Markdown file you can review.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">We will use one question throughout: <em>How should a small Python project use virtual environments and manage dependencies?<\/em> Start with a basic request, then add source parsing and domain filters. You need basic Python knowledge and a Perplexity developer account with API billing set up.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">If you also use AI for everyday research and writing, <a href=\"https:\/\/www.glbgpt.com\/perplexity?inviter=hub_content_perplexity&amp;login=1\">Perplexity pada GlobalGPT<\/a> offers access within an all-in-one AI workspace, reducing the need to switch between separate tools. That consumer workflow is separate from the developer key and API billing used here.<\/p>\n\n\n\n<p class=\"pat-p pat-quick wp-block-paragraph\"><strong>Jawaban singkat:<\/strong> Gunakan <code>cepat<\/code> Agent preset for a first web-grounded answer, read <code>response.output_text<\/code> for the answer, and read the <code>search_results<\/code> item in <code>response.output<\/code> for sources. Match each inline marker to a source by its returned <code>id<\/code>. A citation means \u201cthe answer points to this result\u201d; it does not guarantee that the page proves every claim.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><strong>Example status:<\/strong> The request syntax follows official documentation checked September 28, 2026. The citation helper has been checked with offline sample data; no paid API request was run for this tutorial. Source records shown in the illustration are explanatory examples.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/www.glbgpt.com\/perplexity?inviter=hub_content_perplexity&amp;login=1\"><img alt=\"\" decoding=\"async\" src=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2025\/10\/image-33.png\" class=\"wp-image-2306\"\/><\/a><\/figure>\n\n\n\n<div class=\"wp-block-buttons is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-3e41869c wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button\"><a class=\"wp-block-button__link has-black-color has-text-color has-background has-link-color has-medium-font-size has-custom-font-size wp-element-button\" href=\"https:\/\/www.glbgpt.com\/perplexity?inviter=hub_content_perplexity&amp;login=1\" style=\"background-color:#fec33a;line-height:1\"><strong>Coba Perplexity Sekarang &gt;<\/strong><\/a><\/div>\n<\/div>\n\n\n\n<ul class=\"wp-block-list pat-list pat-toc\">\n<li><a href=\"#api-choice\">Which Perplexity API should you use?<\/a><\/li>\n\n\n\n<li><a href=\"#setup\">Step 1: Get an API key and set up Python<\/a><\/li>\n\n\n\n<li><a href=\"#first-request\">Step 2: Send your first source-grounded request<\/a><\/li>\n\n\n\n<li><a href=\"#sources\">Step 3: Extract the answer and match its sources<\/a><\/li>\n\n\n\n<li><a href=\"#filters\">Step 4: Improve the answer with search filters<\/a><\/li>\n\n\n\n<li><a href=\"#export\">Step 5: Save a complete answer with clickable sources<\/a><\/li>\n\n\n\n<li><a href=\"#advanced\">Streaming and structured output<\/a><\/li>\n\n\n\n<li><a href=\"#cost-errors\">Costs and common errors<\/a><\/li>\n\n\n\n<li><a href=\"#migration\">Moving from an older Sonar tutorial<\/a><\/li>\n\n\n\n<li><a href=\"#faq\">Pertanyaan yang Sering Diajukan<\/a><\/li>\n<\/ul>\n\n\n\n<h2 id=\"api-choice\" class=\"wp-block-heading pat-h2\">Perplexity API tutorial: choose the right API<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">These are developer interfaces. The chat product may package similar capabilities differently; our overview of <a href=\"https:\/\/www.glbgpt.com\/hub\/what-are-the-main-features-of-perplexity-ai\/\">Fitur-fitur Perplexity<\/a> describes the user-facing experience.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Perplexity\u2019s <a href=\"https:\/\/docs.perplexity.ai\/docs\/getting-started\/quickstart\">official API quickstart<\/a> groups its developer products by the job you need to complete. Choose the smallest surface that produces the data your application actually needs.<\/p>\n\n\n\n<figure class=\"wp-block-table pat-table\"><table><thead><tr><th>API<\/th><th>Paling cocok<\/th><th>What your code receives<\/th><\/tr><\/thead><tbody><tr><td><strong>Agen<\/strong><\/td><td>A finished answer grounded in web search or other tools<\/td><td>Typed output items, answer text and search result records<\/td><\/tr><tr><td><strong>Pencarian<\/strong><\/td><td>Your application will rank, filter or summarize results itself<\/td><td>Search results without asking Perplexity to write the final answer<\/td><\/tr><tr><td><strong>Router<\/strong><\/td><td>Route requests to a suitable model<\/td><td>A model response selected through the router<\/td><\/tr><tr><td><strong>Embeddings<\/strong><\/td><td>Semantic retrieval and a RAG index<\/td><td>Vectors for your own search layer<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">This tutorial chooses Agent because the result you want is a finished answer plus its sources. For the broader research workflow, see our guide to <a href=\"https:\/\/www.glbgpt.com\/hub\/how-to-use-perplexity-for-research\/\">using Perplexity for research<\/a>.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><strong>About Sonar:<\/strong> Arus <a href=\"https:\/\/docs.perplexity.ai\/docs\/agent-api\/migrate-from-sonar\/overview\">migration overview<\/a> says Sonar Chat Completions remains supported and recommends Agent for all new projects. This tutorial therefore uses Agent. Do not infer that an old integration has stopped working from a deadline quoted in an older tutorial.<\/p>\n\n\n\n<h2 id=\"setup\" class=\"wp-block-heading pat-h2\">Step 1: Get an API key and set up Python<\/h2>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Create the key and prepare billing<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><a href=\"https:\/\/console.perplexity.ai\/\">Open the Perplexity API console<\/a>, choose or create the project you will use, configure API billing, and create a key. Keep it server-side. The developer APIs are billed separately from consumer subscriptions; check your project balance before making requests.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Install the official SDK<\/h3>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>python -m venv .venv\n\n# macOS\/Linux: choose this activation command\nsource .venv\/bin\/activate\n\n# Windows PowerShell: use this command instead\n.venv\\Scripts\\Activate.ps1\n\npython -m pip install --upgrade perplexityai<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Use Python 3.10 or newer for the code below. Activate the environment using the command for your operating system, then install the SDK. If PowerShell blocks activation, use <code>.venv\\Scripts\\python.exe<\/code> in place of <code>Python<\/code> for installation and script execution.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The package name is <code>perplexityai<\/code>, while the Python import is <code>kebingungan<\/code>. Use a virtual environment so this tutorial&#8217;s dependencies do not change another project.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The example uses a coding question because its claims can be checked against maintained documentation. Our guide to <a href=\"https:\/\/www.glbgpt.com\/hub\/is-perplexity-good-for-coding\/\">Perplexity for coding<\/a> explains where research support fits alongside testing code yourself.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Set the API key as an environment variable<\/h3>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code># macOS\/Linux\nexport PERPLEXITY_API_KEY=\"your-key-here\"\n\n# Windows PowerShell\n$env:PERPLEXITY_API_KEY = \"your-key-here\"<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The SDK reads <code>KUNCI API PERPLEXITY<\/code> when you create the client. Do not paste the key into a notebook, commit it to Git, or send it to a browser. If you use a .env file locally, keep that file out of version control and load it with your chosen environment-variable library.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img fetchpriority=\"high\" decoding=\"async\" width=\"848\" height=\"632\" src=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-sdk-authentication.webp\" alt=\"Official Perplexity documentation showing the Python SDK installation and PERPLEXITY_API_KEY environment variable.\" class=\"wp-image-20033\" srcset=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-sdk-authentication.webp 848w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-sdk-authentication-300x224.webp 300w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-sdk-authentication-16x12.webp 16w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-sdk-authentication-767x572.webp 767w\" sizes=\"(max-width: 848px) 100vw, 848px\" \/><\/figure>\n\n\n\n<h2 id=\"first-request\" class=\"wp-block-heading pat-h2\">Step 2: Send your first source-grounded request<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><a href=\"https:\/\/docs.perplexity.ai\/docs\/agent-api\/quickstart\">The Agent API quickstart<\/a> documents <code>POST https:\/\/api.perplexity.ai\/v1\/agent<\/code>. Its Python client wraps the request with <code>client.responses.create()<\/code>. The <code>cepat<\/code> preset enables web search by default, which keeps the first example small.<\/p>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>from perplexity import Perplexity\n\nclient = Perplexity()\n\nquestion = (\n    \"How should a small Python project use virtual environments \"\n    \"and manage dependencies?\"\n)\n\nresponse = client.responses.create(\n    preset=\"fast\",\n    input=question,\n)\n\nprint(response.output_text)<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Save the snippet as <code>first_request.py<\/code>, lalu jalankan <code>python first_request.py<\/code> in the same terminal where you set the key. A successful call should print nonempty answer text. If you need evidence for the result, continue to the source extraction step; text alone is not a completed check.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The convenience property <code>output_text<\/code> gives you the final answer text. Keep the whole response object as well: source records and usage details are in the typed output, not in the printed paragraph alone.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">The same request with cURL<\/h3>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>curl https:\/\/api.perplexity.ai\/v1\/agent \\\n  -H \"Authorization: Bearer $PERPLEXITY_API_KEY\" \\\n  -H \"Content-Type: application\/json\" \\\n  -d '{\n    \"preset\": \"fast\",\n    \"input\": \"How should a small Python project use virtual environments and manage dependencies?\"\n  }'<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">This cURL example uses Bash syntax for macOS\/Linux or a Bash-compatible shell. Each run makes another billable request. Use it only when you need to isolate an endpoint problem from a Python problem; do not run every version just to follow along.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Server-side JavaScript equivalent<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Node.js 18+ includes <code>fetch<\/code>, so a server-side smoke test needs no extra library. Read the raw <code>output<\/code> items in the JSON response; the Python SDK provides the convenient <code>output_text<\/code> accessor.<\/p>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>const res = await fetch(\"https:\/\/api.perplexity.ai\/v1\/agent\", {\n  method: \"POST\",\n  headers: {\n    Authorization: `Bearer ${process.env.PERPLEXITY_API_KEY}`,\n    \"Content-Type\": \"application\/json\"\n  },\n  body: JSON.stringify({\n    preset: \"fast\",\n    input: \"How should a small Python project use virtual environments and manage dependencies?\"\n  })\n});\nif (!res.ok) throw new Error(`Perplexity HTTP ${res.status}`);\nconsole.log(await res.json());<\/code><\/pre>\n\n\n\n<h2 id=\"sources\" class=\"wp-block-heading pat-h2\">Step 3: Extract the answer and match its sources<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><a href=\"https:\/\/docs.perplexity.ai\/docs\/agent-api\/migrate-from-sonar\/how-to#inline-citations\">The official citation guidance<\/a> distinguishes the prose from its evidence records. Sources arrive in an <code>output<\/code> item with type <code>search_results<\/code>. Read that item\u2019s <code>hasil<\/code> and match each citation marker to the result\u2019s <code>id<\/code>. A result has a title and URL; handle absent values before displaying it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" width=\"848\" height=\"880\" src=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-inline-citations.webp\" alt=\"Official Perplexity documentation explaining numbered citations, source-typed citations and matching markers to search result IDs.\" class=\"wp-image-20034\" srcset=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-inline-citations.webp 848w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-inline-citations-289x300.webp 289w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-inline-citations-12x12.webp 12w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-inline-citations-767x796.webp 767w\" sizes=\"(max-width: 848px) 100vw, 848px\" \/><\/figure>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">That last detail matters. Do not assume \u201ccitation 1\u201d is always the first element in an array after another tool adds an item. Build a lookup by ID, then resolve the markers that actually appear in the answer.<\/p>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>def field(obj, name, default=None):\n    return obj.get(name, default) if isinstance(obj, dict) else getattr(obj, name, default)\n\ndef source_index(output):\n    sources = {}\n    for item in output or &#91;]:\n        if field(item, \"type\") != \"search_results\":\n            continue\n        for result in field(item, \"results\", &#91;]) or &#91;]:\n            sid, url = field(result, \"id\"), field(result, \"url\")\n            if sid is None:\n                continue\n            key = str(sid)\n            if key in sources and field(sources&#91;key], \"url\") != url:\n                raise ValueError(f\"Conflicting URLs for source ID {key}\")\n            sources&#91;key] = result\n    return sources\n\nsources = source_index(response.output)\nprint(sources.get(\"7\"))  # A source ID, not array position 7<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The complete script below recognizes <code>[1]<\/code> dan <code>[web:1]<\/code>, the documented citation forms for the common presets. The helper above handles both SDK objects and dictionaries, and refuses conflicting URLs assigned to the same ID. It does not renumber results or guess a missing source.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">For a broader discussion of citation quality, see our <a href=\"https:\/\/www.glbgpt.com\/hub\/perplexity-citation-accuracy\/\">Perplexity citation accuracy guide<\/a>. A citation is an audit trail; your application still needs a policy for stale pages, duplicate URLs and unsupported claims.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">For example, if the returned array contains ID 2 followed by ID 7, the marker <code>[7]<\/code> must link to ID 7. If the same URL appears under IDs 7 and 9, you can display that URL once while preserving both IDs. If ID 8 never arrives, keep <code>[8]<\/code> visible and flag it as unmatched.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" width=\"1024\" height=\"597\" src=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-citation-id-workflow-1024x597.webp\" alt=\"Citation 7 maps to the search result with ID 7 even when a different result is listed first.\" class=\"wp-image-20035\" srcset=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-citation-id-workflow-1024x597.webp 1024w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-citation-id-workflow-300x175.webp 300w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-citation-id-workflow-768x448.webp 768w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-citation-id-workflow-18x10.webp 18w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-citation-id-workflow.webp 1440w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 id=\"filters\" class=\"wp-block-heading pat-h2\">Step 4: Improve the answer with search filters<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Once the basic request works, constrain the search when your question has a clear authority boundary. For Python packaging, the language documentation and packaging guide are better starting points than an arbitrary tutorial.<\/p>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>response = client.responses.create(\n    preset=\"fast\",\n    input=(\n        \"How should a small Python project use virtual environments \"\n        \"and manage dependencies? Cite the guidance you use.\"\n    ),\n    tools=&#91;\n        {\n            \"type\": \"web_search\",\n            \"filters\": {\n                \"search_domain_filter\": &#91;\n                    \"docs.python.org\",\n                    \"packaging.python.org\",\n                ]\n            },\n        }\n    ],\n)<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The filter belongs inside the web-search tool configuration. The migration documentation also describes <code>search_recency_filter<\/code> for time-sensitive questions. Use recency when the question depends on recent releases or news; a very narrow domain and date combination can leave you with too little evidence.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">For a question about releases in the past month, add <code>\"search_recency_filter\": \"month\"<\/code> beside <code>search_domain_filter<\/code>. Leave it out of the virtual-environment example: stable documentation may have an older publication date. If you switch from a preset to a directly selected model, explicitly include the web-search tool.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Compare filtered and unfiltered results during development. If the answer changes, record why: a filter may remove useful context, or it may remove low-authority pages that were distracting the model. This is a quality decision, not a guarantee that every remaining page is correct.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">If your application needs only ranked links and snippets, call the <a href=\"https:\/\/docs.perplexity.ai\/docs\/getting-started\/quickstart\">Search API<\/a> and perform the synthesis yourself. That separation can make review and caching easier. For a wider comparison with Google, see <a href=\"https:\/\/www.glbgpt.com\/hub\/how-is-perplexity-different-than-google\/\">Perbedaan antara Perplexity dan Google<\/a>.<\/p>\n\n\n\n<h2 id=\"export\" class=\"wp-block-heading pat-h2\">Step 5: Save a complete answer with clickable sources<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Save the following as <code>perplexity_tutorial.py<\/code>. It is self-contained: it includes the source helper, applies the Python documentation domain filter, turns matched citation markers into links, groups duplicate URLs without losing IDs, and saves the complete SDK response alongside Markdown.<\/p>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>from pathlib import Path\nfrom collections import defaultdict\nfrom urllib.parse import urlsplit\nimport json\nimport re\n\nQUESTION = (\n    \"How should a small Python project use virtual environments \"\n    \"and manage dependencies?\"\n)\n# Supports the documented &#91;1] and &#91;web:1] forms in prose answers.\nCITATION_RE = re.compile(r\"(?&lt;!!)\\&#91;(?:web:)?(\\d+)\\](?!\\()\")\n\ndef field(obj, name, default=None):\n    return obj.get(name, default) if isinstance(obj, dict) else getattr(obj, name, default)\n\ndef source_index(output):\n    sources = {}\n    for item in output or &#91;]:\n        if field(item, \"type\") != \"search_results\":\n            continue\n        for result in field(item, \"results\", &#91;]) or &#91;]:\n            sid, url = field(result, \"id\"), field(result, \"url\")\n            if sid is None:\n                continue\n            key = str(sid)\n            if key in sources and field(sources&#91;key], \"url\") != url:\n                raise ValueError(f\"Conflicting URLs for source ID {key}\")\n            sources&#91;key] = result\n    return sources\n\ndef safe_url(value):\n    if not isinstance(value, str):\n        return None\n    parsed = urlsplit(value)\n    if parsed.scheme not in (\"http\", \"https\") or not parsed.netloc:\n        return None\n    # Protect a Markdown angle-bracket destination.\n    return value.replace(\"&lt;\", \"%3C\").replace(\"&gt;\", \"%3E\").replace(\" \", \"%20\")\n\ndef md_label(value):\n    return re.sub(r\"(&#91;\\\\\\&#91;\\]&lt;&gt;])\", r\"\\\\\\1\", str(value).replace(\"\\n\", \" \"))\n\ndef export_markdown(answer, output, question=QUESTION):\n    sources = source_index(output)\n    cited = list(dict.fromkeys(CITATION_RE.findall(answer)))\n    missing = &#91;\n        sid for sid in cited\n        if sid not in sources or not safe_url(field(sources&#91;sid], \"url\"))\n    ]\n    def link(match):\n        sid = match.group(1)\n        url = safe_url(field(sources.get(sid, {}), \"url\"))\n        return f\"&#91;{match.group(0)&#91;1:-1]}](&lt;{url}&gt;)\" if url else match.group(0)\n\n    # Keep every ID when the same URL appears under different IDs.\n    grouped = defaultdict(list)\n    titles = {}\n    for sid in cited:\n        source = sources.get(sid, {})\n        url = safe_url(field(source, \"url\"))\n        if url:\n            grouped&#91;url].append(sid)\n            titles.setdefault(url, field(source, \"title\") or url)\n\n    lines = &#91;\"# Source-grounded answer\", \"\", question, \"\",\n             CITATION_RE.sub(link, answer), \"\", \"## Cited sources\", \"\"]\n    for url, ids in grouped.items():\n        lines.append(f\"- IDs {', '.join(ids)}: &#91;{md_label(titles&#91;url])}](&lt;{url}&gt;)\")\n    if missing:\n        lines.append(\"- Unmatched or unusable source IDs: \" + \", \".join(missing))\n    if not cited:\n        lines.append(\"- No recognized inline citations; evidence not verified.\")\n    lines.extend(&#91;\"\", \"Source links require review; matching IDs does not verify claims.\"])\n    return \"\\n\".join(lines) + \"\\n\"\n\ndef main():\n    from perplexity import Perplexity\n    response = Perplexity().responses.create(\n        preset=\"fast\",\n        input=QUESTION,\n        tools=&#91;{\"type\": \"web_search\", \"filters\": {\n            \"search_domain_filter\": &#91;\"docs.python.org\", \"packaging.python.org\"]\n        }}],\n    )\n    answer = response.output_text or \"\"\n    if not answer.strip():\n        raise RuntimeError(\"No answer text returned; inspect response status and errors.\")\n    Path(\"perplexity-response.json\").write_text(\n        response.model_dump_json(indent=2), encoding=\"utf-8\"\n    )\n    Path(\"perplexity-answer.md\").write_text(\n        export_markdown(answer, response.output), encoding=\"utf-8\"\n    )\n    print(\"Saved perplexity-answer.md and perplexity-response.json\")\n\nif __name__ == \"__main__\":\n    main()\n\n<\/code><\/pre>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Jalankan <code>python perplexity_tutorial.py<\/code> from your project directory. It writes <code>perplexity-answer.md<\/code> dan <code>perplexity-response.json<\/code> there, replacing earlier files with those names. The Markdown contains the question, linked answer, cited sources and any unmatched IDs. The JSON keeps the returned output and usage for diagnosis.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The parser targets the documented single-marker forms in prose. It is not a full Markdown parser: if your application asks for arbitrary Markdown, code samples or citations such as <code>[1,2]<\/code>, add a syntax-aware renderer and tests for that format. Do not silently reinterpret unfamiliar markers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Check whether the linked page supports the answer<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">For this example, inspect whether the cited documentation actually explains environment isolation and dependency installation. A link to the Python homepage is less useful than the relevant documentation section. If an answer recommends a specific command, check its operating system and Python version before running it. Mark claims that the cited text does not support as unresolved.<\/p>\n\n\n\n<h2 id=\"advanced\" class=\"wp-block-heading pat-h2\">Add streaming or structured output when needed<\/h2>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Streaming<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Streaming improves perceived latency for a chat interface. The Agent migration guide documents text delta events such as <code>response.output_text.delta<\/code>. Render those deltas as they arrive, but collect the final response items as well so your UI can show sources after the answer finishes. A stream of text alone is not a source list.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The following is text display only. Keep the non-streaming export for your first complete source record; a production streaming implementation must also collect the tool\/output events and handle interrupted or failed runs.<\/p>\n\n\n\n<pre class=\"wp-block-code pat-code\"><code>stream = client.responses.create(\n    preset=\"fast\", input=question, stream=True\n)\nfor event in stream:\n    if event.type == \"response.output_text.delta\":\n        print(event.delta, end=\"\", flush=True)<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Hasil terstruktur<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Use a structured response when downstream code needs fields such as <code>jawaban<\/code>, <code>confidence_note<\/code> dan <code>follow_up_questions<\/code>. Keep source IDs as a separate field or preserve the original Agent output so your schema does not hide the evidence. Validate the schema on your own test cases before relying on it in a workflow.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The migration reference retains <code>response_format<\/code> dengan <code>type: \"json_schema\"<\/code> for structured output. Define required fields and disallow unexpected properties where appropriate. Schema validity checks shape, not truth: source IDs still need to resolve to actual returned records, and a model-written confidence label is not an accuracy measurement.<\/p>\n\n\n\n<h2 id=\"cost-errors\" class=\"wp-block-heading pat-h2\">Perplexity API costs and common errors<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><a href=\"https:\/\/docs.perplexity.ai\/docs\/getting-started\/pricing\">Perplexity\u2019s official pricing page<\/a>, checked September 28, 2026, separates Agent model tokens from tool invocations. The following are USD tool\/search charges. The <code>cepat<\/code> Agent preset and <code>search_type: \"fast\"<\/code> are different configuration choices; do not infer the search rate from the preset name.<\/p>\n\n\n\n<figure class=\"wp-block-table pat-table\"><table><thead><tr><th>Billable action<\/th><th>USD per action<\/th><th>USD per 1,000<\/th><\/tr><\/thead><tbody><tr><td>Agent standard web search<\/td><td>$0.0025<\/td><td>$2.50<\/td><\/tr><tr><td>Agent Fast Search invocation<\/td><td>$0.001<\/td><td>$1.00<\/td><\/tr><tr><td>Agent URL fetch<\/td><td>$0.0005<\/td><td>$0.50<\/td><\/tr><tr><td>Search API successful request<\/td><td>$0.005<\/td><td>$5.00<\/td><\/tr><tr><td>Search API with Fast Search<\/td><td>$0.001<\/td><td>$1.00<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"736\" height=\"1024\" src=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-tool-search-pricing-736x1024.webp\" alt=\"Official Perplexity pricing tables showing Agent tool invocation prices and Search API prices per 1,000 requests.\" class=\"wp-image-20036\" srcset=\"https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-tool-search-pricing-736x1024.webp 736w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-tool-search-pricing-216x300.webp 216w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-tool-search-pricing-9x12.webp 9w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-tool-search-pricing-768x1068.webp 768w, https:\/\/wp.glbgpt.com\/wp-content\/uploads\/2026\/09\/perplexity-api-official-tool-search-pricing.webp 848w\" sizes=\"(max-width: 736px) 100vw, 736px\" \/><\/figure>\n\n\n\n<p class=\"pat-p wp-block-paragraph\"><strong>Contoh:<\/strong> 1,000 standard web-search invocations plus 1,000 URL fetches cost <strong>$3.00 in tool charges<\/strong> ($2.50 + $0.50), before model tokens or other charges. That is an assumed workload, not a measured cost for the tutorial. A single Agent request may invoke multiple tools. When available, inspect <code>usage.cost.total_cost<\/code> on the completed response.<\/p>\n\n\n\n<figure class=\"wp-block-table pat-table\"><table><thead><tr><th>Gejala<\/th><th>What to check first<\/th><\/tr><\/thead><tbody><tr><td>401 or 403<\/td><td>Environment variable name, key validity, account access and whether the request is reaching the current endpoint.<\/td><\/tr><tr><td>Billing or quota error<\/td><td>API credits, payment setup, model\/tool charges and account limits. A Perplexity consumer plan does not automatically mean API credits.<\/td><\/tr><tr><td>429<\/td><td>Rate limits and retry behavior. Use bounded exponential backoff and avoid replaying a paid request blindly.<\/td><\/tr><tr><td>Timeout<\/td><td>Network path, prompt size, tool count and client timeout settings. Log a request ID if the SDK exposes one, but never log the key.<\/td><\/tr><tr><td>Answer has no matching sources<\/td><td>Inspect the raw <code>response.output<\/code>, citation syntax and source IDs; mark the answer unverified instead of fabricating links.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Search API bills successful requests, including those that return no results; its request price has no additional token charge. Agent costs depend on model tokens and tools. Keep these billing units separate when comparing an answer workflow with a search-only workflow.<\/p>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">For an API budget comparison, our <a href=\"https:\/\/www.glbgpt.com\/hub\/perplexity-api-cost-2025\/\">Panduan Biaya API Perplexity<\/a> adds background, while the official pricing page remains the authority for current rates.<\/p>\n\n\n\n<h2 id=\"migration\" class=\"wp-block-heading pat-h2\">Moving from an older Sonar tutorial<\/h2>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Many search results still show the older Chat Completions shape. The Agent migration guide uses a direct conceptual mapping. If you are comparing consumer model labels with API presets, our explainer on <a href=\"https:\/\/www.glbgpt.com\/hub\/what-llm-does-perplexity-use\/\">apa yang digunakan LLM Perplexity<\/a> provides context, but it is not a substitute for the Agent API documentation.<\/p>\n\n\n\n<figure class=\"wp-block-table pat-table\"><table><thead><tr><th>Older Sonar pattern<\/th><th>Agent API pattern<\/th><\/tr><\/thead><tbody><tr><td><code>pesan<\/code><\/td><td><code>masukan<\/code><\/td><\/tr><tr><td><code>model<\/code><\/td><td><code>preset<\/code><\/td><\/tr><tr><td><code>choices[0].message.content<\/code><\/td><td><code>output_text<\/code><\/td><\/tr><tr><td>Older citation array assumptions<\/td><td><code>search_results<\/code> output item and ID-based matching<\/td><\/tr><tr><td>Search parameters on the request<\/td><td><code>tools=[{\"type\":\"web_search\",\"filters\":{...}}]<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Read the live <a href=\"https:\/\/docs.perplexity.ai\/docs\/agent-api\/migrate-from-sonar\/overview\">migration overview<\/a> dan <a href=\"https:\/\/docs.perplexity.ai\/docs\/agent-api\/migrate-from-sonar\/how-to\">migration details<\/a> before changing production code. The preset mappings are a starting point, not a promise that old and new models produce identical quality, latency or cost.<\/p>\n\n\n\n<h2 id=\"faq\" class=\"wp-block-heading pat-h2\">Pertanyaan yang Sering Diajukan<\/h2>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Is the Perplexity API free?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">The APIs are pay-as-you-go according to the current quickstart and pricing documentation. Do not assume that a consumer subscription, a trial or a promotional credit makes every API request free.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Do I need Perplexity Pro to use the API?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">API access and a consumer Pro plan are separate products. Create API credentials and check API billing for the developer account you plan to use.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Should I use Agent API or Search API?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Choose Agent when you want a finished grounded answer with sources. Choose Search when your application should own ranking, filtering and synthesis. You can combine Search with your own model if you need strict control over every generation step.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Can I call Perplexity from JavaScript?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Yes. Use the server-side Node.js example above or the official JavaScript SDK. Keep the key on the server. When reading raw JSON, inspect the typed output items and apply the same source-ID mapping rules.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Are Perplexity citations guaranteed to be correct?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">No. A citation tells you which returned result the answer points to. Your application or reviewer must still check freshness, authority and whether the page supports the exact claim.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Why did my parser find no sources?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">Inspect the raw output item types, confirm that web search ran, and check whether your regular expression matches the preset&#8217;s citation form. If no marker appears, save the answer as unverified and investigate the request rather than adding a guessed link.<\/p>\n\n\n\n<h3 class=\"wp-block-heading pat-h3\">Can GlobalGPT replace a Perplexity API key?<\/h3>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">GlobalGPT is an all-in-one AI workspace for everyday AI use. Its subscription and model access are separate from a Perplexity developer account, API key and API credits; this Python tutorial uses the developer account.<\/p>\n\n\n\n<h2 id=\"checklist\" class=\"wp-block-heading pat-h2\">A practical launch checklist<\/h2>\n\n\n\n<ul class=\"wp-block-list pat-list pat-checklist\">\n<li>Create a server-side key and confirm API billing.<\/li>\n\n\n\n<li>Run the minimal <code>cepat<\/code> request before adding filters or streaming.<\/li>\n\n\n\n<li>Store the complete response, not only the visible answer text.<\/li>\n\n\n\n<li>Match citation markers to returned source IDs and flag unmatched markers.<\/li>\n\n\n\n<li>Review the cited pages for claim support, freshness and duplicate URLs.<\/li>\n\n\n\n<li>Recheck the live Agent, pricing and migration documentation before deploying.<\/li>\n<\/ul>\n\n\n\n<p class=\"pat-p wp-block-paragraph\">You now have the core pattern for a reliable Perplexity API integration: ask a focused question, let the Agent API search, keep the answer and evidence together, and make every citation auditable. From here, add caching, retries and your own review rules around the same source-ID workflow.<\/p>\n\n\n\n<script type=\"application\/ld+json\">{\n    \"@context\": \"https:\\\/\\\/schema.org\",\n    \"@type\": \"FAQPage\",\n    \"mainEntity\": [\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Is the Perplexity API free?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"The APIs are pay-as-you-go according to the current quickstart and pricing documentation. Do not assume that a consumer subscription, a trial or a promotional credit makes every API request free.\"\n            }\n        },\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Do I need Perplexity Pro to use the API?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"API access and a consumer Pro plan are separate products. Create API credentials and check API billing for the developer account you plan to use.\"\n            }\n        },\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Should I use Agent API or Search API?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"Choose Agent when you want a finished grounded answer with sources. Choose Search when your application should own ranking, filtering and synthesis. You can combine Search with your own model if you need strict control over every generation step.\"\n            }\n        },\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Can I call Perplexity from JavaScript?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"Yes. Use the server-side Node.js example above or the official JavaScript SDK. Keep the key on the server. When reading raw JSON, inspect the typed output items and apply the same source-ID mapping rules.\"\n            }\n        },\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Are Perplexity citations guaranteed to be correct?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"No. A citation tells you which returned result the answer points to. Your application or reviewer must still check freshness, authority and whether the page supports the exact claim.\"\n            }\n        },\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Why did my parser find no sources?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"Inspect the raw output item types, confirm that web search ran, and check whether your regular expression matches the preset's citation form. If no marker appears, save the answer as unverified and investigate the request rather than adding a guessed link.\"\n            }\n        },\n        {\n            \"@type\": \"Question\",\n            \"name\": \"Can GlobalGPT replace a Perplexity API key?\",\n            \"acceptedAnswer\": {\n                \"@type\": \"Answer\",\n                \"text\": \"GlobalGPT is an all-in-one AI workspace for everyday AI use. Its subscription and model access are separate from a Perplexity developer account, API key and API credits; this Python tutorial uses the developer account.\"\n            }\n        }\n    ]\n}<\/script>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>","protected":false},"excerpt":{"rendered":"<p>PERPLEXITY API \u00b7 PYTHON TUTORIAL \u00b7 2026 This Perplexity API tutorial shows you how to build a Python script that returns an answer with clickable web sources. Use the Agent API to send a question, read the answer, match its citation markers to source IDs, and export a Markdown file you can review. We will [&hellip;]<\/p>","protected":false},"author":17,"featured_media":20032,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_seopress_robots_primary_cat":"","_seopress_titles_title":"Perplexity API Tutorial: Build Answers with Sources","_seopress_titles_desc":"Perplexity API tutorial for Python: get a key, build answers with clickable sources, and match citation IDs correctly. Follow the complete script step by step.","_seopress_robots_index":"","footnotes":""},"categories":[7],"tags":[],"class_list":["post-20031","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai-chat"],"acf":[],"_links":{"self":[{"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/posts\/20031","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/users\/17"}],"replies":[{"embeddable":true,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/comments?post=20031"}],"version-history":[{"count":2,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/posts\/20031\/revisions"}],"predecessor-version":[{"id":20038,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/posts\/20031\/revisions\/20038"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/media\/20032"}],"wp:attachment":[{"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/media?parent=20031"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/categories?post=20031"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wp.glbgpt.com\/id\/wp-json\/wp\/v2\/tags?post=20031"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}