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

# Migrating from Exa

> Guide for switching from Exa Search, Contents, and Answer to Linkup Search and Fetch

## Overview

Switch from Exa to Linkup's [**Search**](/pages/documentation/endpoints/search/overview), [**Fetch**](/pages/documentation/endpoints/fetch/overview), and [**Research**](/pages/documentation/endpoints/research/overview) endpoints.

| Exa              | Linkup                                                                  | Role                              |
| ---------------- | ----------------------------------------------------------------------- | --------------------------------- |
| `POST /search`   | `POST /v1/search`                                                       | Web search for AI agents          |
| `POST /contents` | `POST /v1/fetch`                                                        | Clean content from a known URL    |
| `POST /answer`   | `POST /v1/search` with `outputType` `"sourcedAnswer"` or `"structured"` | Cited answer to a question        |
| Exa Agent        | `POST /v1/research`                                                     | Long-running, multi-step research |

Most of the migration is a mechanical rename. The part that is not mechanical is the query itself.

<Warning>
  Exa and Linkup both take a query and return web results, but they expect the query to be written differently. Porting a call across without rewriting the query text does not give you a representative result.
</Warning>

## The core difference

Both APIs put intelligence into the request, in different places.

|                    | Exa                                                                                            | Linkup                                                                  |
| ------------------ | ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
| What the query is  | A description of the page you want to find, often a short noun phrase.                         | An instruction to a search agent: what to find, and what to bring back. |
| Where you steer it | Through parameters: `category`, `contents`, `systemPrompt`, `additionalQueries`, date filters. | Through the query text. The parameter surface is deliberately small.    |
| What you get back  | The best-matching **page**.                                                                    | The best-matching **fact**, with its source.                            |

Anything you expressed to Exa as a parameter needs to be expressed to Linkup as words in `q`. A bare noun phrase gives the Linkup agent very little to plan a retrieval around, so rewriting the query is usually the difference between a mediocre result and a good one.

<Tip>
  **Migrating many call sites?** Give a coding agent a representative sample of your existing Exa calls plus [linkup-for-agents](https://github.com/LinkupPlatform/linkup-for-agents), our context pack of knowledge files, workflow recipes, and skills, and have it produce the translated versions. Install the skills into your project:

  ```bash theme={"system"}
  npx skills add LinkupPlatform/skills
  ```

  See also [Linkup for agents](/pages/documentation/get-started/for-agents).
</Tip>

## Quick start

### Get your API key

<Card title="Get your API key" icon="key" href="https://app.linkup.so" horizontal="True">
  Create a Linkup account for free to get your API key.
</Card>

### Install the SDK

<CodeGroup>
  ```bash Python theme={"system"}
  pip install linkup-sdk
  ```

  ```bash JavaScript theme={"system"}
  npm install linkup-sdk
  ```
</CodeGroup>

## The three settings that matter most

Everything else is optional. These three account for nearly all of the difference in output quality.

### 1. `depth`: choose the retrieval strategy

Exa's `type` is primarily a speed setting. Linkup's `depth` selects *how* the search is carried out, so map it deliberately rather than as a direct rename.

`"flash"` and `"fast"` pass the query as-is to the index, with no LLM. `"standard"` interprets the query and can run parallel sub-searches. `"deep"` adds the ability to open a page it discovered, read it, and decide what to do next. If step two depends on what step one found, or a discovered page has to be read, use `"deep"`.

| Exa `type`                                  | Linkup `depth` | Use when                                                                              |
| ------------------------------------------- | -------------- | ------------------------------------------------------------------------------------- |
| `"instant"`                                 | `"flash"`      | Lowest latency (\<200 ms). Ranked sources and snippets for chat, voice, autocomplete. |
| `"fast"`                                    | `"fast"`       | One-shot retrieval where latency matters (\~1s).                                      |
| `"auto"`                                    | `"standard"`   | Everything you need can be searched for at once, in parallel (1–3s).                  |
| `"deep-lite"`, `"deep"`, `"deep-reasoning"` | `"deep"`       | Step two depends on step one, or a discovered page has to be opened and read (5–30s). |

### 2. `q`: write an instruction, not a description

An Exa query describes a page. A Linkup query gives an instruction. Five moves that reliably improve results with `"standard"` and `"deep"`:

1. **Start with a verb.** Find, search, scrape, extract, compare.
2. **Name the fields you want back.** Linkup returns evidence rather than whole pages, so specify which evidence you need.
3. **Break breadth into explicit facets.** "Run separate searches for X, Y, and Z" is how you get parallel retrieval out of `"standard"`. This replaces Exa's `additionalQueries`.
4. **Repeat your constraints in every facet.** Without it, individual facets drift off-target.
5. **Ask for source URLs.** They make results verifiable at no extra cost.

See [Search best practices](/pages/documentation/endpoints/search/best-practices) for more.

### 3. `outputType`: match it to what your code does next

Exa returns pages and leaves the parsing to you. Linkup asks you to choose the response shape up front.

| If your app…                                | Set `outputType` to                                |
| ------------------------------------------- | -------------------------------------------------- |
| Feeds results into your own LLM or reranker | `"searchResults"`                                  |
| Shows an answer to a person                 | `"sourcedAnswer"`                                  |
| Writes into a CRM, database, or spreadsheet | `"structured"` (requires `structuredOutputSchema`) |

## Migrate your search calls

### Replace your API calls

**Exa**

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST "https://api.exa.ai/search" \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_EXA_KEY" \
    -d '{
      "query": "latest AI news",
      "type": "auto",
      "numResults": 10,
      "contents": { "highlights": true }
    }'
  ```

  ```python Python theme={"system"}
  from exa_py import Exa

  exa = Exa(api_key="YOUR_EXA_KEY")

  response = exa.search(
      "latest AI news",
      type="auto",
      num_results=10,
      contents={"highlights": True},
  )
  print(response)
  ```

  ```javascript JavaScript theme={"system"}
  import Exa from "exa-js";

  const exa = new Exa("YOUR_EXA_KEY");

  const response = await exa.search("latest AI news", {
    type: "auto",
    numResults: 10,
    contents: { highlights: true }
  });
  console.log(response);
  ```
</CodeGroup>

**Linkup**

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST "https://api.linkup.so/v1/search" \
    -H "Authorization: Bearer YOUR_LINKUP_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "q": "Find the latest AI news from the past week. Return the headline, publisher, and date for each story, with the source URL.",
      "depth": "standard",
      "outputType": "searchResults",
      "maxResults": 10
    }'
  ```

  ```python Python theme={"system"}
  from linkup import LinkupClient

  client = LinkupClient(api_key="YOUR_LINKUP_KEY")

  response = client.search(
      query="Find the latest AI news from the past week. Return the headline, publisher, and date for each story, with the source URL.",
      depth="standard",
      output_type="searchResults",
      max_results=10
  )
  print(response)
  ```

  ```javascript JavaScript theme={"system"}
  import { LinkupClient } from 'linkup-sdk';

  const client = new LinkupClient({ apiKey: 'YOUR_LINKUP_KEY' });

  client.search({
    query: 'Find the latest AI news from the past week. Return the headline, publisher, and date for each story, with the source URL.',
    depth: 'standard',
    outputType: 'searchResults',
    maxResults: 10
  }).then(console.log);
  ```
</CodeGroup>

### Parameter mapping

| Exa parameter                | Linkup parameter                                       | Notes                                                                                                                                                                                         |
| ---------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `query`                      | `q`                                                    | Required. **Rewrite it** as an instruction rather than copying it across. See the `q` section above                                                                                           |
| `type`                       | `depth`                                                | Required. Map `"instant"` → `"flash"`, `"fast"` → `"fast"`, `"auto"` → `"standard"`, `"deep-lite"` / `"deep"` / `"deep-reasoning"` → `"deep"`                                                 |
| `numResults`                 | `maxResults`                                           | Rename. `numResults` is not a Linkup parameter                                                                                                                                                |
| `category`                   | Express in `q`                                         | `"company"` / `"people"`: "Find the official company page for…", or pass a LinkedIn URL directly. `"news"` / `"financial report"` / `"publication"`: "…from its most recent earnings release" |
| `additionalQueries`          | Express in `q`                                         | "Run separate searches for: …". Works with `"standard"` and `"deep"`                                                                                                                          |
| `includeDomains`             | `includeDomains`                                       | Array of domain strings, up to 100 entries (Exa allows 1,200). Reserve for domains you specifically need; a general preference such as "prefer official sources" works better in `q`          |
| `excludeDomains`             | `excludeDomains`                                       | Array of domain strings                                                                                                                                                                       |
| `startPublishedDate`         | `fromDate`                                             | ISO 8601 date (`YYYY-MM-DD`), not a full timestamp                                                                                                                                            |
| `endPublishedDate`           | `toDate`                                               | ISO 8601 date (`YYYY-MM-DD`), not a full timestamp                                                                                                                                            |
| `contents.highlights`        | `outputType` `"searchResults"`                         | Linkup's default response shape. Each result includes a `content` snippet                                                                                                                     |
| `contents.text`              | **Fetch**, or `depth` `"deep"`                         | If you have the result URLs, call [**Fetch**](/pages/documentation/endpoints/fetch/overview) on them. To read pages the search discovers, use `"deep"` and say "scrape the page" in `q`       |
| `contents.summary`           | `outputType` `"sourcedAnswer"` or `"structured"`       | A summary schema maps to `structuredOutputSchema`                                                                                                                                             |
| `contents.subpages`          | `depth` `"deep"` + instruction in `q`                  | For example: "if the page links to a pricing page, scrape that too"                                                                                                                           |
| `contents.extras.imageLinks` | `includeImages`                                        | Returns image results when supported                                                                                                                                                          |
| `outputSchema`               | `outputType` `"structured"` + `structuredOutputSchema` | JSON schema passed as a string; the root must be `type` `"object"`. See the [structured output guide](/pages/documentation/tutorials/structured-output-guide)                                 |
| `systemPrompt`               | Express in `q`                                         | "Prefer official sources" becomes a sentence in the query                                                                                                                                     |
| `userLocation`               | Express in `q`                                         | "…for the French market"                                                                                                                                                                      |
| `stream`                     | N/A                                                    | **Search** returns a single synchronous response                                                                                                                                              |
| `x-api-key` header           | `Authorization: Bearer` header                         | —                                                                                                                                                                                             |

### Response format differences

**Exa response structure**

```json theme={"system"}
{
  "requestId": "b5947044c4b78efa9552a7c89b306d95",
  "results": [
    {
      "title": "Page Title",
      "url": "https://example.com",
      "publishedDate": "2026-09-01T00:00:00.000Z",
      "highlights": ["Relevant excerpt..."]
    }
  ],
  "costDollars": { "total": 0.007 }
}
```

**Linkup response structure (`"searchResults"`)**

```json theme={"system"}
{
  "results": [
    {
      "type": "text",
      "name": "Page Title",
      "url": "https://example.com",
      "content": "Relevant content..."
    }
  ]
}
```

**Linkup response structure (`"sourcedAnswer"`)**

```json theme={"system"}
{
  "answer": "Generated answer with citations...",
  "sources": [
    {
      "name": "Page Title",
      "url": "https://example.com",
      "snippet": "Relevant excerpt..."
    }
  ]
}
```

**Linkup response structure (`"structured"`)**

```json theme={"system"}
{
  "customField1": "Extracted data following your schema...",
  "customField2": ["Array", "of", "values"]
}
```

With `includeSources` set to `true`, the response becomes `{ "data": { ... }, "sources": [ ... ] }`. See the [structured output guide](/pages/documentation/tutorials/structured-output-guide).

### Worked example

A typical Exa company search, translated.

**Exa**

```json theme={"system"}
{
  "query": "agtech companies in the US that have raised series A",
  "type": "auto",
  "category": "company",
  "numResults": 10,
  "contents": { "highlights": true }
}
```

**Linkup**

```json theme={"system"}
{
  "q": "Find US-based agtech companies that have raised a Series A round. Run separate searches for: US agtech startups that announced a Series A round; United States agriculture technology companies Series A funding round size and lead investor; American agtech Series A funding announcements in 2025 and 2026. For each company return the company name, headquarters, round size, announcement date, and lead investor, with the source URL for each finding.",
  "depth": "standard",
  "outputType": "searchResults",
  "maxResults": 10
}
```

What changed:

* `category` `"company"` became words in the query.
* `highlights` became `outputType` `"searchResults"`.
* The noun phrase became an instruction with named fields.
* One vague search became three parallel facets, with "US" and "Series A" repeated in all three so no facet drifts off-target.

To write the results straight into a database, switch `outputType` to `"structured"` and pass a `structuredOutputSchema` describing one company per array item.

## Migrate your contents calls

Exa **Contents** returns page content for known URLs. Linkup **Fetch** returns clean markdown for a single public URL (HTML or PDF), with optional JavaScript rendering and optional structured extraction.

<Info>
  **Fetch** accepts one `url` per request. If you currently pass an array of URLs to Exa **Contents**, call Linkup **Fetch** once per URL (or in parallel). For batch workloads, submit your **Fetch** calls through [**Tasks**](/pages/documentation/endpoints/tasks/overview): up to 100 per submission, with the same parameters and pricing as direct `/fetch` calls.
</Info>

### Replace your API calls

**Exa**

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST "https://api.exa.ai/contents" \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_EXA_KEY" \
    -d '{
      "urls": ["https://docs.linkup.so"],
      "text": true
    }'
  ```

  ```python Python theme={"system"}
  from exa_py import Exa

  exa = Exa(api_key="YOUR_EXA_KEY")

  response = exa.get_contents(
      ["https://docs.linkup.so"],
      text=True
  )
  print(response)
  ```

  ```javascript JavaScript theme={"system"}
  import Exa from "exa-js";

  const exa = new Exa("YOUR_EXA_KEY");

  const response = await exa.getContents(["https://docs.linkup.so"], {
    text: true
  });
  console.log(response);
  ```
</CodeGroup>

**Linkup**

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST "https://api.linkup.so/v1/fetch" \
    -H "Authorization: Bearer YOUR_LINKUP_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "url": "https://docs.linkup.so",
      "renderJs": true
    }'
  ```

  ```python Python theme={"system"}
  from linkup import LinkupClient

  client = LinkupClient(api_key="YOUR_LINKUP_KEY")

  response = client.fetch(
      url="https://docs.linkup.so",
      render_js=True,
  )
  print(response)
  ```

  ```javascript JavaScript theme={"system"}
  import { LinkupClient } from 'linkup-sdk';

  const client = new LinkupClient({ apiKey: 'YOUR_LINKUP_KEY' });

  const response = await client.fetch({
    url: 'https://docs.linkup.so',
    renderJs: true,
  });
  console.log(response);
  ```
</CodeGroup>

### Parameter mapping

| Exa parameter               | Linkup parameter          | Notes                                                                                                                                                                                      |
| --------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `urls` / `ids`              | `url`                     | One URL per request. Batch through [**Tasks**](/pages/documentation/endpoints/tasks/overview)                                                                                              |
| `text`                      | N/A                       | `markdown` is always returned                                                                                                                                                              |
| `summary` (with `schema`)   | `schema` + `instructions` | Returns typed JSON in `data` alongside the markdown. Fields not found on the page are omitted, not invented                                                                                |
| `extras.imageLinks`         | `extractImages`           | Returns a separate list of image URLs                                                                                                                                                      |
| `maxAgeHours` / `livecrawl` | N/A                       | **Fetch** retrieves the page in real time                                                                                                                                                  |
| `subpages`                  | N/A                       | **Fetch** reads one URL and does not follow links. Use **Search** with `depth` `"deep"`, or [**Extract**](/pages/documentation/endpoints/extract/overview) (closed beta) for listing pages |
| N/A                         | `renderJs`                | Render client-side JavaScript before extraction                                                                                                                                            |
| N/A                         | `mode`                    | `"pro"` for hard-to-retrieve pages                                                                                                                                                         |

### Response format differences

**Exa response structure**

```json theme={"system"}
{
  "requestId": "b5947044c4b78efa9552a7c89b306d95",
  "results": [
    {
      "id": "https://docs.linkup.so",
      "url": "https://docs.linkup.so",
      "title": "Page Title",
      "text": "Extracted content..."
    }
  ],
  "statuses": [{ "id": "https://docs.linkup.so", "status": "success" }]
}
```

**Linkup response structure**

```json theme={"system"}
{
  "markdown": "# Page title\n\nExtracted content...",
  "images": [
    { "alt": "Logo", "url": "https://example.com/logo.svg" }
  ]
}
```

`markdown` is always returned. `images` is empty unless `extractImages` is `true`. `data` is present only when `schema` is set.

### Example: structured extraction from a known URL

Replaces Exa **Contents** with a `summary` schema.

```bash cURL theme={"system"}
curl -X POST "https://api.linkup.so/v1/fetch" \
  -H "Authorization: Bearer YOUR_LINKUP_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.linkup.so/pricing",
    "renderJs": true,
    "schema": {
      "type": "object",
      "properties": {
        "plans": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "name": { "type": "string" },
              "price": { "type": "string", "description": "Public list price" }
            }
          }
        }
      }
    },
    "instructions": "Express monetary values in USD."
  }'
```

## Migrate your answer calls

Exa **Answer** maps to Linkup **Search** with `outputType` `"sourcedAnswer"`. For a schema-shaped answer, use `outputType` `"structured"` with `structuredOutputSchema` in place of Exa's `outputSchema`. Fold `systemPrompt` and `userLocation` into `q`.

**Exa**

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST "https://api.exa.ai/answer" \
    -H "Content-Type: application/json" \
    -H "x-api-key: YOUR_EXA_KEY" \
    -d '{
      "query": "What is the latest valuation of SpaceX?",
      "systemPrompt": "Prefer official sources."
    }'
  ```

  ```python Python theme={"system"}
  response = exa.answer(
      "What is the latest valuation of SpaceX?",
      system_prompt="Prefer official sources."
  )
  ```

  ```javascript JavaScript theme={"system"}
  const response = await exa.answer("What is the latest valuation of SpaceX?", {
    systemPrompt: "Prefer official sources."
  });
  ```
</CodeGroup>

**Linkup**

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X POST "https://api.linkup.so/v1/search" \
    -H "Authorization: Bearer YOUR_LINKUP_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "q": "What is the latest valuation of SpaceX? Prefer official sources and recent reporting, and include the date of the valuation.",
      "depth": "standard",
      "outputType": "sourcedAnswer"
    }'
  ```

  ```python Python theme={"system"}
  response = client.search(
      query="What is the latest valuation of SpaceX? Prefer official sources and recent reporting, and include the date of the valuation.",
      depth="standard",
      output_type="sourcedAnswer",
  )
  print(response)
  ```

  ```javascript JavaScript theme={"system"}
  const response = await client.search({
    query: 'What is the latest valuation of SpaceX? Prefer official sources and recent reporting, and include the date of the valuation.',
    depth: 'standard',
    outputType: 'sourcedAnswer',
  });
  console.log(response);
  ```
</CodeGroup>

Exa returns `answer` plus `citations`. Linkup returns `answer` plus `sources`, each with `name`, `url`, and `snippet`.

## Migrate your agent calls

For long-running research that Exa Agent handles (deep research, list building, enrichment), use Linkup [**Research**](/pages/documentation/endpoints/research/overview). It returns a sourced answer or structured output (`outputType` `"sourcedAnswer"` or `"structured"`), with `mode` and `reasoningDepth` to pin the type of investigation and its thoroughness. For list building and enrichment over many rows, also see [**Tasks**](/pages/documentation/endpoints/tasks/overview).

## Need help?

* Check our [Quickstart Guide](/pages/documentation/get-started/quickstart)
* Read [Search best practices](/pages/documentation/endpoints/search/best-practices) and [Fetch best practices](/pages/documentation/endpoints/fetch/best-practices)
* Join our [Discord Community](https://discord.gg/9q9mCYJa86)
* Explore the [Search](/pages/documentation/endpoints/search/reference) and [Fetch](/pages/documentation/endpoints/fetch/reference) API references

<Info>
  Your Linkup account starts with \$20 of free credit when you sign up with a professional email address. You can monitor usage and add more credit in the [Billing](https://app.linkup.so/organization/billing) section.
</Info>
