Overview
The Web Search API allows you to perform AI-powered web searches across supported providers (Linkup, Tavily, Exa, Kagi, Perplexity, Valyu, Brave), returning up-to-date information from across the internet. This API supports multiple output formats, date filtering, domain filtering, and provider-specific depth modes. If provider is omitted, Linkup is used by default.
Authentication
Alternatively, you can use Bearer token authentication:
Request Body
The search query to send to the provider
Search provider. Options: “linkup”, “tavily”, “exa”, “kagi”, “perplexity”, “valyu”, “brave”. “openai-native” is not supported on /api/web.
Search depth. Options vary by provider:
Linkup/Tavily/Perplexity/Valyu/Brave: “standard” or “deep”
Exa: “fast”, “auto”, “neural”, “deep” (Exa-only)
Kagi: “standard” or “deep” (“search” source only for deep)
outputType
string
default: "searchResults"
Output format. Options: “searchResults”, “sourcedAnswer”, or “structured”. For Tavily, Exa, Kagi, Perplexity, Valyu, and Brave, outputType must be “searchResults”.
Required when outputType is “structured”. JSON schema string defining the desired response format
Whether to include image results in the search
Kagi only. Source to query: “web”, “news”, or “search” (also accepts “kagi_source”)
Filter results from this date (YYYY-MM-DD format)
Filter results until this date (YYYY-MM-DD format)
Array of domains to exclude from search results
Array of domains to search exclusively
Provider-specific options
Provider-specific fields are accepted at the top level of the request body and only apply to the selected provider.
Perplexity options
{
"maxResults" : 1-20 ,
"maxTokensPerPage" : number ,
"maxTokens" : 1-1000000 ,
"country" : "string" ,
"searchDomainFilter" : [ "domain1.com" , "domain2.com" ],
"searchLanguageFilter" : [ "en" , "de" ]
}
Limits: searchDomainFilter max 20 entries; searchLanguageFilter max 10 entries (ISO 639-1).
Valyu options
{
"searchType" : "all" | "web" ,
"fastMode" : boolean ,
"maxNumResults" : 1-50 ,
"maxPrice" : number ,
"relevanceThreshold" : 0-1 ,
"responseLength" : "short" | "medium" | "large" | "max" | number ,
"countryCode" : "US" ,
"includedSources" : [ "source1.com" ],
"excludedSources" : [ "source2.com" ],
"urlOnly" : boolean ,
"category" : "string"
}
countryCode uses a 2-letter ISO country code.
Tavily options
{
"maxResults" : 0-20 ,
"includeAnswer" : boolean | "basic" | "advanced" ,
"includeRawContent" : boolean | "markdown" | "text" ,
"includeImages" : boolean ,
"includeImageDescriptions" : boolean ,
"includeFavicon" : boolean ,
"topic" : "general" | "news" | "finance" ,
"timeRange" : "day" | "week" | "month" | "year" ,
"startDate" : "YYYY-MM-DD" ,
"endDate" : "YYYY-MM-DD" ,
"chunksPerSource" : 1-3 ,
"country" : "string"
}
Exa options
{
"numResults" : 1-100 ,
"category" : "company" | "research paper" | "news" | "pdf" | "github" | "tweet" | "personal site" | "people" | "financial report" ,
"userLocation" : "US" ,
"additionalQueries" : [ "query2" ],
"startCrawlDate" : "ISO 8601" ,
"endCrawlDate" : "ISO 8601" ,
"startPublishedDate" : "ISO 8601" ,
"endPublishedDate" : "ISO 8601" ,
"includeText" : [ "pattern" ],
"excludeText" : [ "pattern" ],
"livecrawl" : "never" | "fallback" | "always" | "preferred" ,
"livecrawlTimeout" : number ,
"subpages" : number ,
"subpageTarget" : "string" | [ "strings" ]
}
Kagi options
{
"kagiSource" : "web" | "news" | "search"
}
Brave options
{
"count" : 0-20 ,
"maxResults" : 0-20 ,
"offset" : 0-9 ,
"safesearch" : "off" | "moderate" | "strict" ,
"freshness" : "2024-01-01to2024-02-01" ,
"searchLang" : "string" ,
"uiLang" : "string" ,
"eSnippets" : boolean ,
"spellcheck" : boolean ,
"goggles" : "string" | [ "string" ],
"country" : "string"
}
Use either count or maxResults (0-20). searchLang and uiLang are passed to Brave as search_lang and ui_lang. If freshness is not provided but both fromDate and toDate are set, the API builds a Brave freshness range (fromDate + "to" + toDate). For deep searches, Brave defaults extraSnippets to true unless eSnippets is explicitly set.
Response
Search results, answer, or structured data depending on outputType
The search query that was executed
The provider used for the search (may be “brave”)
The search depth used (provider-specific)
ISO 8601 timestamp of when the search was performed
The cost of the search in USD
Search Results (default)
Returns an array of search results with text and image entries:
{
"data" : [
{
"type" : "text" ,
"title" : "Article Title" ,
"url" : "https://example.com/article" ,
"snippet" : "Article snippet..." ,
"publishedAt" : "2025-07-01"
},
{
"type" : "image" ,
"title" : "Image Title" ,
"url" : "https://example.com/image.jpg" ,
"imageUrl" : "https://example.com/image.jpg" ,
"publishedAt" : "2025-07-01"
}
],
"metadata" : {
"query" : "your search query" ,
"provider" : "linkup" ,
"depth" : "standard" ,
"outputType" : "searchResults" ,
"timestamp" : "2025-07-08T09:00:00.000Z" ,
"cost" : 0.006
}
}
Sourced Answer
Returns a comprehensive answer with source citations:
{
"data" : {
"answer" : "The comprehensive answer to your query..." ,
"sources" : [
{
"name" : "Source Name" ,
"url" : "https://example.com" ,
"snippet" : "Relevant snippet from the source..."
}
]
},
"metadata" : {
"query" : "your search query" ,
"provider" : "linkup" ,
"depth" : "standard" ,
"outputType" : "sourcedAnswer" ,
"timestamp" : "2025-07-08T09:00:00.000Z" ,
"cost" : 0.006
}
}
Structured Output
Returns data matching your provided JSON schema:
{
"data" : {
// Your structured data according to the schema
},
"metadata" : {
"query" : "your search query" ,
"provider" : "linkup" ,
"depth" : "standard" ,
"outputType" : "structured" ,
"timestamp" : "2025-07-08T09:00:00.000Z" ,
"cost" : 0.006
}
}
Examples
import requests
import json
# Your API key
api_key = "YOUR_API_KEY"
# API endpoint
url = "https://nano-gpt.com/api/web"
# Headers
headers = {
"Content-Type" : "application/json" ,
"x-api-key" : api_key
}
# Basic search
basic_search = {
"query" : "artificial intelligence trends 2025" ,
"provider" : "linkup"
}
response = requests.post(url, headers = headers, json = basic_search)
results = response.json()
# Print results
if results[ "metadata" ][ "outputType" ] == "searchResults" :
for result in results[ "data" ]:
print ( f "Title: { result[ 'title' ] } " )
print ( f "URL: { result[ 'url' ] } " )
print ( f "Snippet: { result.get( 'snippet' , 'N/A' )[: 200 ] } ..." )
print ( "-" * 50 )
print ( f "Search cost: $ { results[ 'metadata' ][ 'cost' ] } " )
Brave example (request body)
POST /api/web
{
"query" : "latest EU AI Act amendments" ,
"provider" : "brave" ,
"depth" : "standard" ,
"outputType" : "searchResults" ,
"count" : 10 ,
"offset" : 0 ,
"safesearch" : "moderate" ,
"searchLang" : "en" ,
"uiLang" : "en" ,
"freshness" : "2024-01-01to2024-02-01"
}
Advanced Examples
Deep Search with Date Filtering
def deep_search_with_dates ( query , from_date , to_date ):
data = {
"query" : query,
"depth" : "deep" ,
"fromDate" : from_date,
"toDate" : to_date
}
response = requests.post(url, headers = headers, json = data)
return response.json()
# Search for recent research
results = deep_search_with_dates(
"quantum computing breakthroughs" ,
"2025-01-01" ,
"2025-07-01"
)
def extract_structured_data ( query , schema ):
data = {
"query" : query,
"outputType" : "structured" ,
"structuredOutputSchema" : json.dumps(schema)
}
response = requests.post(url, headers = headers, json = data)
return response.json()
# Define schema for extracting programming language data
schema = {
"type" : "object" ,
"properties" : {
"languages" : {
"type" : "array" ,
"items" : {
"type" : "object" ,
"properties" : {
"rank" : { "type" : "number" },
"name" : { "type" : "string" },
"popularityScore" : { "type" : "string" },
"primaryUseCase" : { "type" : "string" }
}
}
}
}
}
# Extract structured data
results = extract_structured_data(
"top 5 programming languages 2025" ,
schema
)
Domain-Specific Search
def search_specific_domains ( query , domains , exclude = False ):
data = {
"query" : query,
"outputType" : "sourcedAnswer"
}
if exclude:
data[ "excludeDomains" ] = domains
else :
data[ "includeDomains" ] = domains
response = requests.post(url, headers = headers, json = data)
return response.json()
# Search only trusted news sources
news_results = search_specific_domains(
"latest tech acquisitions" ,
[ "reuters.com" , "bloomberg.com" , "techcrunch.com" ],
exclude = False
)
# Exclude certain domains
filtered_results = search_specific_domains(
"python tutorials" ,
[ "w3schools.com" , "geeksforgeeks.org" ],
exclude = True
)
Error Handling
400 Bad Request
401 Unauthorized
402 Payment Required
429 Too Many Requests
500 Internal Server Error
{
"error" : "Query parameter is required and must be a string"
}
Rate Limiting
The API is rate-limited to 60 requests per minute per IP address for /api/web. Internal or authenticated requests may have different limits. If you exceed this limit, you’ll receive a 429 status code.
Best Practices
Use Standard Search for General Queries : Standard search is usually cheaper and sufficient for most use cases.
Use Deep Search for Research : Deep search provides more comprehensive results and is ideal for research tasks or when you need extensive information.
Leverage Domain Filtering : Use includeDomains to search specific trusted sources or excludeDomains to filter out unwanted sources.
Date Filtering for Current Events : Use fromDate and toDate to get the most recent information on rapidly evolving topics.
Structured Output for Data Extraction : Use structured output when you need to extract specific data points from search results in a predictable format.
Handle Errors Gracefully : Always implement error handling for rate limits, insufficient balance, and network errors.
Cache Results : Consider caching search results for identical queries to reduce costs and improve performance.
Output Type Selection Guide
searchResults : Best for general searches where you want to see multiple sources and snippets. Ideal for research and exploration.
sourcedAnswer : Best when you want a comprehensive answer synthesized from multiple sources. Great for factual questions and summaries.
structured : Best when you need to extract specific data points in a predictable format. Perfect for data collection and automation.
Pricing
Provider Standard Deep Notes Linkup $0.006 $0.06 Default provider Tavily $0.008 $0.016 Good value, free tier available Exa $0.005 base + $0.001/page For contents retrieval Kagi Web/News $0.002 N/A Cheapest for enrichment Kagi Search $0.025 N/A Full search mode Perplexity $0.005 N/A Flat rate Valyu ~$0.0015/result Variable Dynamic pricing Brave $0.009 $0.009 Flat rate OpenAI Native $0.01 + tokens N/A Per-call fee + model token costs
A minimum balance of $1.00 is required to use the web search API. OpenAI native applies to GPT-5/o-series chat completions only and is not used by /api/web.
Bring your own key (BYOK)
BYOK is supported for Tavily, Exa, Kagi, Perplexity, and Valyu. Linkup and Brave always use NanoGPT’s key and are not BYOK on /api/web. When you supply your own provider key, NanoGPT does not add a platform charge for web search. Configure keys in team settings or pass them via headers.
X-402 payment protocol (optional)
Web search supports anonymous payment via the X-402 protocol. Accepted payment rails include Nano mainnet and Base USDC, using X-PAYMENT header schemes.