> ## Documentation Index
> Fetch the complete documentation index at: https://docs.perplexity.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrate from Sonar to the Agent API

> Move from the Sonar chat completions API to the Agent API. Learn what changes, why it matters, and how to migrate one flow at a time.

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.

<Info>
  **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.
</Info>

<Note>
  Missing a Sonar parameter you rely on? Tell us at [api@perplexity.ai](mailto:api@perplexity.ai) — it helps us prioritize what lands on the Agent API next.
</Note>

## 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](/docs/getting-started/pricing?calc=agent#agent-api-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](/docs/agent-api/tools/overview) such as `web_search` and `fetch_url`, connect [remote MCP servers](/docs/agent-api/tools/mcp), 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

| Capability                                          |       Sonar API       |       Agent API       |
| --------------------------------------------------- | :-------------------: | :-------------------: |
| Perplexity Sonar model                              | <Icon icon="check" /> | <Icon icon="check" /> |
| Third-party models (OpenAI, Anthropic, Google, xAI) |                       | <Icon icon="check" /> |
| Built-in web search                                 | <Icon icon="check" /> | <Icon icon="check" /> |
| People Search                                       |                       | <Icon icon="check" /> |
| Finance Search                                      |                       | <Icon icon="check" /> |
| Code sandbox                                        |                       | <Icon icon="check" /> |
| MCP servers                                         |                       | <Icon icon="check" /> |
| Model fallback                                      |                       | <Icon icon="check" /> |
| Structured outputs                                  | <Icon icon="check" /> | <Icon icon="check" /> |
| Streaming                                           | <Icon icon="check" /> | <Icon icon="check" /> |
| Async mode                                          | <Icon icon="check" /> | <Icon icon="check" /> |

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.

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>
</Columns>

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.

<Columns cols={2}>
  <div>
    **Sonar response**

    ```json expandable theme={null}
    {
      "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}
        // ...
      ]
      // ...
    }
    ```
  </div>

  <div>
    **Agent API response**

    ```json expandable theme={null}
    {
      "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"
      // ...
    }
    ```
  </div>
</Columns>

## 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.

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>
</Columns>

### 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.

<Tip>
  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](/docs/agent-api/conversation-state).
</Tip>

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      # 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
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      # 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
      ```
    </CodeGroup>
  </div>
</Columns>

### 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.

<Tip>
  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.
</Tip>

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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'
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>
</Columns>

<Note>
  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](/api-reference/agent-post) for the full schema.
</Note>

<Warning>
  **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](/api-reference/agent-post) for the full status and `error` schema.
</Warning>

#### 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`.

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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="")
      ```

      ```typescript Typescript theme={null}
      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);
        }
      }
      ```

      ```bash cURL theme={null}
      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
        }'
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
        }
      }
      ```

      ```bash cURL theme={null}
      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
        }'
      ```
    </CodeGroup>
  </div>
</Columns>

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.

<Warning>
  **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.
</Warning>

### 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](/docs/agent-api/tools/web-search) reference for the full set of options.

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>
</Columns>

#### 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.

<CodeGroup>
  ```python Python theme={null}
  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
  ```

  ```typescript Typescript theme={null}
  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
  ```

  ```bash cURL theme={null}
  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
  ```
</CodeGroup>

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](#5-choose-a-preset-and-model)). To get inline citations consistently — whatever preset or model you use — set the rule yourself in `instructions`:

<CodeGroup>
  ```python Python theme={null}
  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
  ```

  ```typescript Typescript theme={null}
  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
  ```

  ```bash cURL theme={null}
  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
  ```
</CodeGroup>

### 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"`.

<Tip>
  Starting points we suggest: `sonar` → `fast`, `sonar-pro` and `sonar-reasoning-pro` → `low`, `sonar-deep-research` → `medium`. These are recommendations, not exact equivalents — see [Choosing a preset](/docs/agent-api/presets#choosing-a-preset) for the trade-offs and [current preset values](/docs/agent-api/presets#current-preset-values) for exactly what each one bundles today.
</Tip>

<Columns cols={2}>
  <div>
    **Sonar**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>

  <div>
    **Agent API**

    <CodeGroup>
      ```python Python theme={null}
      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)
      ```

      ```typescript Typescript theme={null}
      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);
      ```

      ```bash cURL theme={null}
      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
      ```
    </CodeGroup>
  </div>
</Columns>

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.

<Info>
  Presets and models are billed by the model and tools a request actually uses. Check [pricing](/docs/getting-started/pricing?calc=agent#agent-api-pricing) and [rate limits](/docs/admin/rate-limits-usage-tiers#agent-api-rate-limits) before cutting over — output is not byte-identical to Sonar.
</Info>

### 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](/docs/agent-api/output-control#background-runs) for the request shape, polling, reconnect, and the full lifecycle.

## Parameter reference

<Note>
  Missing a Sonar parameter you rely on? Tell us at [api@perplexity.ai](mailto:api@perplexity.ai) — it helps us prioritize what lands on the Agent API next.
</Note>

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 parameter                                                                                                                                                  | Agent API equivalent                                                                                                                |
| ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| [Domain, recency, and date filters](/docs/sonar/filters)<br />(`search_domain_filter`, `search_recency_filter`, `search_*_date_filter`, `last_updated_*_filter`) | Same names, inside the [`web_search` tool's `filters`](/docs/agent-api/tools/web-search#filters)                                    |
| `web_search_options.search_context_size`                                                                                                                         | [`search_context_size`](/docs/agent-api/tools/web-search#configuring-search) on the `web_search` tool, not in `filters`             |
| `web_search_options.user_location`                                                                                                                               | [`user_location`](/docs/agent-api/tools/web-search#location-filter) on the `web_search` tool, not in `filters`                      |
| `num_search_results`                                                                                                                                             | [`max_results`](/docs/agent-api/tools/web-search#number-of-results) on the `web_search` tool — a cap on the total number of results |
| `reasoning_effort`                                                                                                                                               | `reasoning.effort`                                                                                                                  |

**No direct field — map to a tool or pattern:**

| Sonar parameter            | Agent API equivalent                                                                                                                                                                                                                                                                                                       |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `enable_search_classifier` | Provide the [`web_search`](/docs/agent-api/tools/web-search) tool and let the model decide when to search                                                                                                                                                                                                                  |
| `disable_search`           | Omit the [`web_search`](/docs/agent-api/tools/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_mode`              | `web` (the default) is implicit. `academic` has no direct equivalent; use `web_search` with domain filters and prompting. For `sec`, use [Finance Search](/docs/agent-api/tools/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](/docs/agent-api/presets) (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_questions` | Prompt the model to end with a few follow-up questions, optionally through a [structured output](/docs/agent-api/output-control#structured-outputs) 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](/docs/sonar/media#receiving-images) (`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](/docs/sonar/media#receiving-videos) (`return_videos`, `num_videos`, `media_response`) — not supported; the Agent API returns no `videos`.

<Tip>
  See the [Web Search](/docs/agent-api/tools/web-search) reference for the full set of `web_search` options.
</Tip>

### Multimodal input

| Sonar content part     | Agent API                                                                               |
| ---------------------- | --------------------------------------------------------------------------------------- |
| `image_url`            | [`input_image` content part](/docs/agent-api/image-attachments) (data URI or HTTPS URL) |
| `file_url` / `pdf_url` | No equivalent                                                                           |
| `video_url`            | No equivalent                                                                           |
