How to migrate from Exa to Linkup
The Linkup Team
If you built on Exa and need higher accuracy, lower cost, or enterprise compliance, the migration to Linkup takes under an hour.
Exa and Linkup both accept a query and return web results. That similarity is misleading - they put the intelligence in different places, and copying a call from one to the other without changing anything will produce worse results than either API is capable of. This guide walks through exactly what needs to change.
Why a copy-paste test fails
Exa's model is search-engine-style: write a short phrase that describes the page you want, and tune everything else through parameters - category, contents, systemPrompt, date filters. The query itself is minimal. The configuration does the steering.
Linkup's model inverts this. There are far fewer parameters on purpose. Everything that Exa expressed as configuration, Linkup expresses through the query text. The query is an instruction to a search agent, not a description of a target page.
When you paste an Exa call into Linkup without rewriting it, two things go wrong at once. The parameters you relied on to steer Exa don't exist in Linkup, so they silently drop. And Linkup receives a bare noun phrase with no instructions, so its agent has very little to plan a retrieval around. The result is that Linkup gets judged on a query that was never written for Linkup.
The fix is straightforward. Three things need to change.
1. Rewrite the query as an instruction
This is the most important step. An Exa query describes a page you want to find. A Linkup query tells the agent what to find and what to bring back.
Five moves that reliably improve results:
- Start with a verb. "Find," "search," "extract," "compare" - anything that makes it clear the query is an order, not a description.
- Name the fields you want back. Linkup returns evidence rather than whole pages, so specify which evidence you need. "Return the company name, headquarters, round size, and source URL for each result" is far more useful than leaving it implicit.
- Break broad searches into parallel facets. Instead of one vague search, write "run separate searches for X, Y, and Z." This is how you get parallel retrieval out of
standarddepth. - Repeat your constraints in every facet. This is the easiest one to skip, and it has a measurable effect on accuracy. If you want US-based results, write "US" in every sub-search, not just the first one.
- Ask for source URLs. They cost nothing extra and make results verifiable at a glance.
Anything Exa expressed through category, systemPrompt, or userLocation belongs in the query text instead. "Category: company" becomes "Find the official company page for...". "User location: France" becomes "...for the French market."
2. Map type to depth carefully
These look like the same setting. They are not.
Exa's type is a speed dial - instant is faster, deep is slower but considers more. Linkup's depth is a capability dial. standard and deep are not different quality levels for the same operation; they are fundamentally different retrieval modes.
standard searches in parallel and works from the results. deep adds the ability to follow a result, open the page, and read its full contents before deciding what to do next. If your task requires scraping a page that the search itself has to discover, standard cannot do this - it's a hard limit, not a quality difference. Use deep whenever step two depends on what step one found.
The mapping:
Exa type | Linkup depth | When to use |
|
| One simple fact, latency matters |
|
| Everything can be searched in parallel |
|
| Step 2 depends on what step 1 found |
3. Choose outputType to match your downstream code
Exa always returns pages and lets you parse them. Linkup asks you to choose the response shape up front. The wrong choice can look like a bug in your integration.
If you feed results into your own LLM or reranker, use outputType: "searchResults" - this is also the default. If you show an answer to a person, use sourcedAnswer. If you write into a CRM, database, or spreadsheet, use structured and provide a schema - the schema root must be an object, and there is no depth or property-count limit. For long-running multi-step research tasks, the /research endpoint is the right tool rather than chaining /search calls.
The Exa field that maps to this is contents: highlights maps to searchResults, summary maps to sourcedAnswer. If you were using outputSchema on the Exa side, the equivalent is structuredOutputSchema.
What a translation looks like
Here is the same call, before and after.
// Before - Exa
{
"query": "agtech companies in the US that have raised series A",
"type": "auto",
"category": "company",
"numResults": 10,
"contents": { "highlights": true }
}
// After - Linkup
{
"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 an output type. 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 individual facet can drift off-target. numResults was renamed to maxResults.
Parameter reference
The full mapping for the parameters you're most likely to be using:
Exa | Linkup | Notes |
|
| Rewrite as an instruction - don't copy it across |
|
| Capability dial, not speed dial. See above. |
|
| Straight rename |
| Fold into | "Find the official company page for..." or pass a LinkedIn URL directly |
| Same names | Max 100 domains. Use for specific known domains only - general preferences like "prefer official sources" work better in |
|
| Use YYYY-MM-DD format. For soft recency preferences, writing it in |
|
| If you already have the URL, use |
|
| Linkup's default, at no extra charge |
|
| Root must be an object. No nesting-depth or property-count limit |
| Direct swap when you already have the URL |
One note on contents.maxAgeHours and livecrawl: there is no equivalent because Linkup searches live by default. There is nothing to configure and no freshness surcharge.
Migrating a lot of queries
If you have many call sites to migrate, the fastest path is to give a coding agent a representative sample of your existing Exa calls along with linkup-for-agents - Linkup's context pack of knowledge files, workflow recipes, and prompt templates - and have it produce the translated versions. The results are consistently better than rewriting call sites by hand.
The full parameter documentation and SDK references are in Linkup's official docs. If you migrated from another provider, the same approach applies - Linkup's guide for migrating from Tavily covers the equivalent steps for that API.




