NativePort
← How-to

How to Use Tavily Search in Python

Run a Tavily search that returns relevance-scored results plus an optional grounded answer in one call — the exact request shape, in plain Python against NativePort's gateway.

Tavily was built for retrieval pipelines rather than adapted for one: every result carries a relevance score, and a single call can also return an LLM-ready answer synthesized from what it found, so there’s no separate summarization step. NativePort proxies Tavily’s API under one key and balance; this guide covers the /search endpoint, which covers the bulk of what agents built on Tavily actually call.

Why through NativePort

Tavily bills in credits and reports what each response used. Here you pay exactly that, and your balance covers your scraping and your model calls too.

What you’ll need

  • A NativePort API key. Sign up to get a key and $5 in credits.
  • Python 3, standard library only — the example below uses urllib.request, no pip install.
  • Your key exported as an environment variable, never hardcoded:
export NATIVEPORT_API_KEY="np_..."

What’s admitted

/tavily/<endpoint> forwards to the matching endpoint on api.tavily.com, POST only — the method Tavily’s own action endpoints document:

  • search — the general-purpose query endpoint this guide covers.
  • extract — pull the full content of one or more known URLs, no search involved.
  • crawl — start from a URL and follow links outward, gathering content per page.
  • map — discover a site’s URL structure without extracting content.

Tavily’s /usage endpoint (account-wide credit and limit metadata) is deliberately not exposed — it’s account-wide billing state, not a search interface, and a per-client key has no business reading the shared account’s usage. Any endpoint outside the four above 404s before it ever reaches Tavily.

import json
import os
import urllib.request

GATEWAY = "https://api.nativeport.ai"
API_KEY = os.environ["NATIVEPORT_API_KEY"]


def search(query, **kwargs):
    body = json.dumps({"query": query, **kwargs}).encode()
    req = urllib.request.Request(f"{GATEWAY}/tavily/search", data=body, method="POST")
    req.add_header("Authorization", f"Bearer {API_KEY}")
    req.add_header("Content-Type", "application/json")
    req.add_header("User-Agent", "nativeport-python-guide/1.0")
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read())


result = search("what changed in the latest Kubernetes release", max_results=5)
for item in result["results"]:
    print(f"{item['score']:.2f}  {item['title']}  {item['url']}")

query is the only required field; everything else is Tavily’s own optional parameter set, forwarded verbatim — max_results (0–20, default 5), topic (general, news, finance), time_range, include_domains/exclude_domains, and the two fields worth calling out on their own:

  • include_answer: true (or "basic"/"advanced") adds a synthesized answer string to the response, built from the result set Tavily itself just retrieved.
  • include_raw_content: true (or "markdown"/"text") adds each result’s full page content alongside its snippet, so a follow-up extract call isn’t needed just to get the whole page.

The response is Tavily’s own shape, untouched:

{
  "query": "what changed in the latest Kubernetes release",
  "answer": "The release adds ...",
  "results": [
    {
      "title": "Kubernetes v1.34 release notes",
      "url": "https://kubernetes.io/releases/...",
      "content": "This release introduces...",
      "score": 0.94
    }
  ],
  "response_time": 1.12,
  "usage": {"credits": 1},
  "request_id": "..."
}

answer is present only when include_answer was set on the request; usage likewise only shows up when include_usage: true is on the request — set it if you want the response itself to confirm what a call cost, since that’s also the same field the gateway bills from (see below).

Errors you’ll actually hit

  • 402 Payment Required: the NativePort balance is at $0. Every request answers this deterministically rather than degrading, so top up and retry.
  • 401 Unauthorized: the Authorization: Bearer <NATIVEPORT_API_KEY> header is missing, malformed, or doesn’t resolve to an account.
  • 403 Forbidden: the key is valid but the account isn’t active.
  • 404 on /tavily/usage or anything outside search/extract/crawl/map: not admitted at the gateway — see “What’s admitted” above.
  • An empty results array with a 200: not a gateway error — Tavily itself found nothing for that query at the requested search_depth. Retrying with search_depth: "advanced" or a broader time_range is a Tavily-side tuning question, not something to retry blindly.

Security

Treat NATIVEPORT_API_KEY like any other credential: environment variable or secret store, never a literal string in source, never logged. Tavily’s own credential never reaches your process — the gateway injects it server-side.

What this costs

The gateway bills from usage.credits when the response carries it (set include_usage: true to guarantee that), falling back to a 1-credit charge if it’s missing — at Tavily’s own per-credit rate, no NativePort markup. A basic-depth search is 1 credit; search_depth: "advanced" is 2; extract, crawl and map price per URL or page processed rather than per call. Current per-credit pricing is on the Tavily provider page. A zero balance answers every request with 402 instead of degrading. Full breakdown: pricing.

Where to go next