Skip to main content
The Agent API is our unified interface for building agentic applications — Sonar leveled up. It keeps Sonar’s grounded, real-time web search and adds any frontier model, fully configurable tools and parameters, and transparent usage-based pricing, all behind a single endpoint.
The Sonar API remains supported. You can migrate one flow at a time. New projects should start on the Agent API to access multi-provider models, built-in tools, and the latest platform features.
Missing a Sonar parameter you rely on? Tell us at api@perplexity.ai — it helps us prioritize what lands on the Agent API next.

Why migrate

The Agent API is Sonar leveled up: the same grounded, real-time web search, now on an endpoint built for agentic work — with any frontier model behind it and every knob exposed. It keeps everything Sonar does well and adds capabilities that are hard to build on top of chat completions:
  • Same search, more models — Keep the same grounded, real-time web search, now with any frontier model behind it (OpenAI, Anthropic, Google, xAI) alongside Perplexity’s own. Sonar serves only Perplexity’s Sonar models.
  • Fully configurable — Set search depth, reasoning, and multi-step behavior with tools and parameters, instead of picking a fixed Sonar tier you can’t tune.
  • Transparent, usage-based pricing — Model tokens bill at direct provider rates with no markup, plus a flat fee per tool invocation, so you pay for what you actually use — instead of Sonar’s per-request search fee tiered by search context size. See pricing.
  • Agentic by default — The model runs an autonomous agentic loop within a single request: it reasons, calls a tool, observes the result, and repeats until it has enough to answer. It can call built-in tools such as web_search and fetch_url, connect remote MCP servers, and run your own custom functions, chaining multiple tool calls without a round trip back to your code.
  • Better performance — The Agent API produces higher-quality answers than chat completions on the same task.
  • Model fallback — Pass a prioritized list of models and let the API serve the first available one, instead of managing failover yourself.
  • Typed output items — The response is a typed output array of items (messages, tool calls, and search/tool results) rather than a flat choices array, which better represents multi-step agent behavior.
  • Future-proof — New models and platform features land on the Agent API first.

Sonar vs Agent API

CapabilitySonar APIAgent API
Perplexity Sonar model
Third-party models (OpenAI, Anthropic, Google, xAI)
Built-in web search
People Search
Finance Search
Code sandbox
MCP servers
Model fallback
Structured outputs
Streaming
Async mode
Both APIs generate output from a model, but they shape a request differently. Sonar takes a messages array and returns a choices array. The Agent API splits the two: you send an input (a string or an array of input items) and read a typed output array of items — messages, tool calls, and search or tool results — that represents each step the model took.
Sonar
from perplexity import Perplexity

client = Perplexity()

completion = client.chat.completions.create(
    model="sonar",
    messages=[{"role": "user", "content": "What are the latest developments in AI agents?"}]
)
print(completion.choices[0].message.content)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const completion = await client.chat.completions.create({
  model: 'sonar',
  messages: [{ role: 'user', content: 'What are the latest developments in AI agents?' }]
});
console.log(completion.choices[0].message.content);
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar",
    "messages": [
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq
Agent API
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    preset="fast",
    input="What are the latest developments in AI agents?"
)
print(response.output_text)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  preset: 'fast',
  input: 'What are the latest developments in AI agents?'
});
console.log(response.output_text);
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "preset": "fast",
    "input": "What are the latest developments in AI agents?"
  }' | jq
The response objects differ the same way. Sonar returns a flat choices array with the answer at choices[0].message.content and sources at the top level. The Agent API returns a typed output array: a message item holds the text (in an output_text content block) and a separate search_results item holds the sources.
Sonar response
{
  "id": "ec83afe3-b589-48e0-9bcb-c60f7bbc7418",
  "choices": [
    {
      // ...
      "message": {"content": "The latest developments in AI agents (as of July 2026) center on **transitioning from demos to production** ...", "role": "assistant", "reasoning_steps": null, "tool_call_id": null, "tool_calls": null},
      "finish_reason": "stop"
    }
  ],
  "created": 1783605927,
  "model": "sonar",
  "citations": [
    "https://aiagentstore.ai/ai-agent-news/this-week",
    "https://aiagentindex.mit.edu"
    // ...
  ],
  "object": "chat.completion",
  "search_results": [
    {"title": "Daily AI Agent News - Last 7 Days", "url": "https://aiagentstore.ai/ai-agent-news/this-week", "date": "2026-07-08", "last_updated": "2026-07-07", "snippet": "Pentagon pilots AI agents to speed software approvals. ...", "source": "web", "place_metadata": null}
    // ...
  ]
  // ...
}
Agent API response
{
  "id": "resp_fa73e017-332d-43b0-91d1-dfc4130e0ffb",
  "created_at": 1783605927,
  "model": "openai/gpt-5.4-mini",
  "object": "response",
  "output": [
    {
      "results": [
        {"id": 1, "snippet": "This transition from one-shot intelligence to endurance ...", "title": "State of AI Agents 2026: Autonomy is Here - Prosus", "url": "https://www.prosus.com/news-insights/2026/state-of-ai-agents-2026-autonomy-is-here", "date": "2026-02-26", "last_updated": "2026-06-25", "source": "web"}
        // ...
      ],
      "type": "search_results"
      // ...
    },
    {
      "id": "msg_00315d52-3120-40c6-95e4-4d4b89f5d2e1",
      "content": [
        {"text": "AI agents have moved rapidly beyond chatbots into autonomous, multi‑agent systems [1][2] ...", "type": "output_text", "annotations": []}
      ],
      "role": "assistant",
      "status": "completed",
      "type": "message"
    }
  ],
  "status": "completed"
  // ...
}

Migrating from chat completions

1. Update the endpoint and method

Point requests at /v1/agent and switch from chat.completions.create() to responses.create(). For plain text, the Agent API takes the same role/content items, so the body barely changes: pass the array as input instead of messages and rename the model.
Sonar
from perplexity import Perplexity

client = Perplexity()

completion = client.chat.completions.create(
    model="sonar",
    messages=[
        {"role": "system", "content": "You are a concise research assistant."},
        {"role": "user", "content": "What are the latest developments in AI agents?"},
    ],
)
print(completion.choices[0].message.content)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const completion = await client.chat.completions.create({
  model: 'sonar',
  messages: [
    { role: 'system', content: 'You are a concise research assistant.' },
    { role: 'user', content: 'What are the latest developments in AI agents?' },
  ],
});
console.log(completion.choices[0].message.content);
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar",
    "messages": [
      {"role": "system", "content": "You are a concise research assistant."},
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq
Agent API
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    model="perplexity/sonar",
    input=[
        {"role": "system", "content": "You are a concise research assistant."},
        {"role": "user", "content": "What are the latest developments in AI agents?"},
    ],
)
print(response.output_text)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  model: 'perplexity/sonar',
  input: [
    { role: 'system', content: 'You are a concise research assistant.' },
    { role: 'user', content: 'What are the latest developments in AI agents?' },
  ],
});
console.log(response.output_text);
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "perplexity/sonar",
    "input": [
      {"role": "system", "content": "You are a concise research assistant."},
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq

2. Map messages to input

Sonar takes your prompt as a messages array; the Agent API takes it as input. For simple single-turn prompts, pass a plain string. To preserve a system prompt or a multi-turn transcript, pass an array of input items or use the top-level instructions field.
Continuing a multi-turn conversation? The shortcut is previous_response_id — point a new request at a completed prior response and skip resending the transcript. You can also replay the prior turns (including assistant items) in input yourself. See Conversation state.
Sonar
from perplexity import Perplexity

client = Perplexity()

# Single user turn
completion = client.chat.completions.create(
    model="sonar",
    messages=[{"role": "user", "content": "What are the latest developments in AI agents?"}]
)

# System guidance plus a user turn
completion = client.chat.completions.create(
    model="sonar",
    messages=[
        {"role": "system", "content": "You are a concise research assistant."},
        {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
)
print(completion.choices[0].message.content)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

// Single user turn
const simple = await client.chat.completions.create({
  model: 'sonar',
  messages: [{ role: 'user', content: 'What are the latest developments in AI agents?' }]
});

// System guidance plus a user turn
const completion = await client.chat.completions.create({
  model: 'sonar',
  messages: [
    { role: 'system', content: 'You are a concise research assistant.' },
    { role: 'user', content: 'What are the latest developments in AI agents?' }
  ]
});
console.log(completion.choices[0].message.content);
# System guidance plus a user turn
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar",
    "messages": [
      {"role": "system", "content": "You are a concise research assistant."},
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq
Agent API
from perplexity import Perplexity

client = Perplexity()

# Simple string input
response = client.responses.create(
    model="perplexity/sonar",
    input="What are the latest developments in AI agents?"
)

# System guidance plus a user turn
response = client.responses.create(
    model="perplexity/sonar",
    instructions="You are a concise research assistant.",
    input=[
        {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
)
print(response.output_text)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

// Simple string input
const simple = await client.responses.create({
  model: 'perplexity/sonar',
  input: 'What are the latest developments in AI agents?'
});

// System guidance plus a user turn
const response = await client.responses.create({
  model: 'perplexity/sonar',
  instructions: 'You are a concise research assistant.',
  input: [
    { role: 'user', content: 'What are the latest developments in AI agents?' }
  ]
});
console.log(response.output_text);
# System guidance plus a user turn
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "perplexity/sonar",
    "instructions": "You are a concise research assistant.",
    "input": [
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq

3. Update output handling

Sonar returns a choices array, with the final text at choices[0].message.content. The Agent API returns a typed output array of items — the answer text lives in a message item’s content block with type: "output_text". Iterate output and branch on each item’s type to read messages, tool calls, and search or tool results. Reasoning is surfaced only as response.reasoning.* streaming events — it never appears as an output item.
In the SDK, response.output_text is a convenience property that concatenates those output_text blocks for you, so you don’t have to walk the array by hand for the common case.
Sonar
from perplexity import Perplexity

client = Perplexity()

completion = client.chat.completions.create(
    model="sonar",
    messages=[{"role": "user", "content": "What are the latest developments in AI agents?"}],
)
print(completion.choices[0].message.content)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const completion = await client.chat.completions.create({
  model: 'sonar',
  messages: [{ role: 'user', content: 'What are the latest developments in AI agents?' }],
});
console.log(completion.choices[0].message.content);
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar",
    "messages": [
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq -r '.choices[0].message.content'
Agent API
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    model="perplexity/sonar",
    input="What are the latest developments in AI agents?",
)

# Convenience: the concatenated answer text
print(response.output_text)

# Or pick items out of the typed output array yourself
message = next(item for item in response.output if item.type == "message")
print(message.content[0].text)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  model: 'perplexity/sonar',
  input: 'What are the latest developments in AI agents?',
});
console.log(response.output_text);
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "perplexity/sonar",
    "input": "What are the latest developments in AI agents?"
  }' | jq
Metering usage? The response usage object is renamed on the Agent API (input_tokens/output_tokens instead of prompt_tokens/completion_tokens, and the search count moves under usage.tool_calls_details). See the API reference for the full schema.
Check response.status, not the HTTP code. Cancelled, failed, and truncated runs all return HTTP 200, so a consumer that keys off the HTTP status treats them as success. Branch on response.status (completed, incomplete, failed, cancelled) instead; truncation shows up as status: "incomplete", replacing Sonar’s finish_reason: "length". See the API reference for the full status and error schema.

Streaming

Sonar streaming returns incremental chunks with a delta.content field on each choice. The Agent API streams typed server-sent events. Update stream consumers to branch on each event’s type.
Sonar
from perplexity import Perplexity

client = Perplexity()

stream = client.chat.completions.create(
    model="sonar",
    messages=[{"role": "user", "content": "Explain quantum computing"}],
    stream=True
)
for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const stream = await client.chat.completions.create({
  model: 'sonar',
  messages: [{ role: 'user', content: 'Explain quantum computing' }],
  stream: true
});
for await (const chunk of stream) {
  if (chunk.choices[0]?.delta?.content) {
    process.stdout.write(chunk.choices[0].delta.content);
  }
}
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar",
    "messages": [
      {"role": "user", "content": "Explain quantum computing"}
    ],
    "stream": true
  }'
Agent API
from perplexity import Perplexity

client = Perplexity()

stream = client.responses.create(
    model="perplexity/sonar",
    input="Explain quantum computing",
    stream=True
)
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const stream = await client.responses.create({
  model: 'perplexity/sonar',
  input: 'Explain quantum computing',
  stream: true
});
for await (const event of stream) {
  if (event.type === 'response.output_text.delta') {
    process.stdout.write(event.delta);
  }
}
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "perplexity/sonar",
    "input": "Explain quantum computing",
    "stream": true
  }'
For the answer text, consume response.output_text.delta events; tool calls and reasoning arrive as their own events (response.output_item.*, response.reasoning.*), not as text deltas.
Handle every terminal event, or your consumer hangs. A stream ends with exactly one of response.completed, response.failed, response.incomplete, or response.cancelled (transport failures surface as a generic error). Treat all of them as end-of-stream, not just response.completed, and ignore event types you don’t recognize.

4. Web search and citations

web_search runs Perplexity’s own search — the same real-time, grounded web search that was built into Sonar, now exposed as a tool. In Sonar it was on by default; on the Agent API you turn it on by adding web_search to the request’s tools — until you do, the model answers from its own knowledge without searching (that is why the earlier steps returned ungrounded answers). The tool also carries the search controls, so Sonar’s top-level search_recency_filter and search_domain_filter move into its filters. See the Web Search reference for the full set of options.
Sonar
from perplexity import Perplexity

client = Perplexity()

completion = client.chat.completions.create(
    model="sonar",
    messages=[{"role": "user", "content": "Latest renewable energy policy updates"}],
    search_recency_filter="month",
    search_domain_filter=["iea.org", "energy.gov"]
)
print(completion.choices[0].message.content)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const completion = await client.chat.completions.create({
  model: 'sonar',
  messages: [{ role: 'user', content: 'Latest renewable energy policy updates' }],
  search_recency_filter: 'month',
  search_domain_filter: ['iea.org', 'energy.gov']
});
console.log(completion.choices[0].message.content);
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar",
    "messages": [
      {"role": "user", "content": "Latest renewable energy policy updates"}
    ],
    "search_recency_filter": "month",
    "search_domain_filter": ["iea.org", "energy.gov"]
  }' | jq
Agent API
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    model="perplexity/sonar",
    input="Latest renewable energy policy updates",
    tools=[
        {
            "type": "web_search",
            "filters": {
                "search_domain_filter": ["iea.org", "energy.gov"],
                "search_recency_filter": "month"
            }
        }
    ]
)
print(response.output_text)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  model: 'perplexity/sonar',
  input: 'Latest renewable energy policy updates',
  tools: [
    {
      type: 'web_search' as const,
      filters: {
        search_domain_filter: ['iea.org', 'energy.gov'],
        search_recency_filter: 'month'
      }
    }
  ]
});
console.log(response.output_text);
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "perplexity/sonar",
    "input": "Latest renewable energy policy updates",
    "tools": [
      {
        "type": "web_search",
        "filters": {
          "search_domain_filter": ["iea.org", "energy.gov"],
          "search_recency_filter": "month"
        }
      }
    ]
  }' | jq

Inline citations

The fast preset cites out of the box — call preset="fast" and the answer comes back with inline [n] markers, like Sonar did.
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    preset="fast",
    input="What are the latest developments in AI agents?",
)
print(response.output_text)  # answer with inline [n] markers
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  preset: 'fast',
  input: 'What are the latest developments in AI agents?',
});
console.log(response.output_text); // answer with inline [n] markers
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "preset": "fast",
    "input": "What are the latest developments in AI agents?"
  }' | jq
The [n] markers are embedded directly in the answer text (response.output_text), not in a separate field. The sources they point to come back separately in an output item with type: "search_results" — read them from its results, each carrying an id, and each [n] marker maps to the result whose id is n. A preset’s system prompt is tuned by Perplexity and can change over time, and it differs from one preset to another (see Choose a preset and model). To get inline citations consistently — whatever preset or model you use — set the rule yourself in instructions:
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    model="perplexity/sonar",
    instructions=(
        "Base every factual statement on the numbered web search results provided. "
        "Before finalizing, verify each claim against the sources: re-read the cited results and "
        "confirm they actually support the statement; if a claim is not directly supported, drop "
        "it or soften it rather than guessing. "
        "After each sentence that uses information from those results, cite the exact source "
        "number(s) in square brackets right after the statement, like [1] or [1][2], with no "
        "space before the bracket. Cite the one to three sources that most directly support the "
        "statement, and only cite a source that actually contains that information. Do not cite a "
        "source you did not use, do not invent source numbers, and do not add a separate "
        "references section."
    ),
    input="What are the latest developments in AI agents?",
    tools=[{"type": "web_search"}],
)

print(response.output_text)  # answer with inline [n] markers
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  model: 'perplexity/sonar',
  instructions:
    'Base every factual statement on the numbered web search results provided. ' +
    'Before finalizing, verify each claim against the sources: re-read the cited results and ' +
    'confirm they actually support the statement; if a claim is not directly supported, drop ' +
    'it or soften it rather than guessing. ' +
    'After each sentence that uses information from those results, cite the exact source ' +
    'number(s) in square brackets right after the statement, like [1] or [1][2], with no ' +
    'space before the bracket. Cite the one to three sources that most directly support the ' +
    'statement, and only cite a source that actually contains that information. Do not cite a ' +
    'source you did not use, do not invent source numbers, and do not add a separate ' +
    'references section.',
  input: 'What are the latest developments in AI agents?',
  tools: [{ type: 'web_search' as const }],
});

console.log(response.output_text); // answer with inline [n] markers
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "perplexity/sonar",
    "instructions": "Base every factual statement on the numbered web search results provided. Before finalizing, verify each claim against the sources: re-read the cited results and confirm they actually support the statement; if a claim is not directly supported, drop it or soften it rather than guessing. After each sentence that uses information from those results, cite the exact source number(s) in square brackets right after the statement, like [1] or [1][2], with no space before the bracket. Cite the one to three sources that most directly support the statement, and only cite a source that actually contains that information. Do not cite a source you did not use, do not invent source numbers, and do not add a separate references section.",
    "input": "What are the latest developments in AI agents?",
    "tools": [{ "type": "web_search" }]
  }' | jq

5. Choose a preset and model

A preset bundles a model, search config, tools, and reasoning behind one name that Perplexity keeps tuning — sensible defaults out of the box, no wiring required. Migrating a Sonar tier is usually a one-liner: for example, sonar-deep-research maps to preset="medium".
Starting points we suggest: sonarfast, sonar-pro and sonar-reasoning-prolow, sonar-deep-researchmedium. These are recommendations, not exact equivalents — see Choosing a preset for the trade-offs and current preset values for exactly what each one bundles today.
Sonar
from perplexity import Perplexity

client = Perplexity()

completion = client.chat.completions.create(
    model="sonar-deep-research",
    messages=[{"role": "user", "content": "What are the latest developments in AI agents?"}]
)
print(completion.choices[0].message.content)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const completion = await client.chat.completions.create({
  model: 'sonar-deep-research',
  messages: [{ role: 'user', content: 'What are the latest developments in AI agents?' }]
});
console.log(completion.choices[0].message.content);
curl https://api.perplexity.ai/v1/sonar \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "sonar-deep-research",
    "messages": [
      {"role": "user", "content": "What are the latest developments in AI agents?"}
    ]
  }' | jq
Agent API
from perplexity import Perplexity

client = Perplexity()

response = client.responses.create(
    preset="medium",
    input="What are the latest developments in AI agents?",
)
print(response.output_text)
import Perplexity from '@perplexity-ai/perplexity_ai';

const client = new Perplexity();

const response = await client.responses.create({
  preset: 'medium',
  input: 'What are the latest developments in AI agents?',
});
console.log(response.output_text);
curl https://api.perplexity.ai/v1/agent \
  -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "preset": "medium",
    "input": "What are the latest developments in AI agents?"
  }' | jq
Presets aren’t all-or-nothing: pass any field alongside one to override just that part — a different model, max_steps, custom web_search filters — and everything else keeps the preset’s defaults.
Presets and models are billed by the model and tools a request actually uses. Check pricing and rate limits before cutting over — output is not byte-identical to Sonar.

6. Migrate async requests (optional)

Sonar’s async API maps to Agent API background runs: submit with background: true and poll the response by id. See Background Runs for the request shape, polling, reconnect, and the full lifecycle.

Parameter reference

Missing a Sonar parameter you rely on? Tell us at api@perplexity.ai — it helps us prioritize what lands on the Agent API next.
Map the common Sonar parameters to their Agent API equivalents. Some are direct field moves; others are the closest Agent API pattern, and those rows call out the missing 1:1 equivalent.

Standard OpenAI parameters

These standard OpenAI parameters carry over unchanged: temperature, top_p, and stream. A couple of parameters change on the Agent API:
  • max_tokens is renamed to max_output_tokens.
  • response_format keeps the same shape for json_schema, and structured outputs are not restricted to specific Perplexity models on the Agent API. Sonar’s response_format.type: "regex" has no Agent API equivalent — redesign regex flows around a JSON schema.

Sonar-specific parameters

Direct equivalents (some renamed or relocated):
Sonar parameterAgent API equivalent
Domain, recency, and date filters
(search_domain_filter, search_recency_filter, search_*_date_filter, last_updated_*_filter)
Same names, inside the web_search tool’s filters
web_search_options.search_context_sizesearch_context_size on the web_search tool, not in filters
web_search_options.user_locationuser_location on the web_search tool, not in filters
num_search_resultsmax_results on the web_search tool — a cap on the total number of results
reasoning_effortreasoning.effort
No direct field — map to a tool or pattern:
Sonar parameterAgent API equivalent
enable_search_classifierProvide the web_search tool and let the model decide when to search
disable_searchOmit the web_search tool. With a preset, the preset’s tools stay enabled — tools: [] does not clear them and there is currently no public way to disable preset tools; max_tool_calls: 0 disables all tool calls as a blunt workaround
search_modeweb (the default) is implicit. academic has no direct equivalent; use web_search with domain filters and prompting. For sec, use Finance Search for structured finance data or web_search filtered to SEC sources for filing/source retrieval
search_type (Pro Search)No direct equivalent — map "fast" to the fast preset (formerly fast-search) and "pro" to preset: "low" (formerly pro-search) for the multi-step Pro Search behavior, or set max_steps on an explicit model to bound multi-step search. There is no "auto" classifier equivalent
return_related_questionsPrompt the model to end with a few follow-up questions, optionally through a structured output schema
No Agent API equivalent — drop these:
  • search_language_filter.
  • stream_mode — Agent API streaming always emits typed SSE events with reasoning and tool activity as separate events, closest to Sonar’s concise mode; there is no full-mode inline-metadata format.
  • Image results (return_images, num_images, image_domain_filter, image_format_filter, web_search_options.image_results_enhanced_relevance) — not supported; the Agent API returns no images.
  • Video results (return_videos, num_videos, media_response) — not supported; the Agent API returns no videos.
See the Web Search reference for the full set of web_search options.

Multimodal input

Sonar content partAgent API
image_urlinput_image content part (data URI or HTTPS URL)
file_url / pdf_urlNo equivalent
video_urlNo equivalent