curl --request GET \
--url https://api.prefetch.io/search \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.prefetch.io/search"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.prefetch.io/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.prefetch.io/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.prefetch.io/search"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.prefetch.io/search")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.prefetch.io/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"query": "stripe pricing",
"type": "web",
"count": 2,
"scraped": 0,
"warnings": [],
"results": [
{
"position": 1,
"url": "https://stripe.com/pricing",
"title": "Pricing & Fees | Stripe",
"description": "Find Stripe fees and pricing information…",
"date": null,
"source": null,
"image_url": null
},
{
"position": 2,
"url": "https://docs.stripe.com/billing/subscriptions/pricing",
"title": "Pricing models | Stripe Documentation",
"description": "Learn about common pricing models…",
"date": null,
"source": null,
"image_url": null
}
]
},
"meta": {
"requestId": "6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b",
"durationMs": 1840
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}GET /search
Search the web and get each result’s title, URL and snippet — optionally with every result page scraped. 2 credits per 10 results.
curl --request GET \
--url https://api.prefetch.io/search \
--header 'X-API-Key: <api-key>'import requests
url = "https://api.prefetch.io/search"
headers = {"X-API-Key": "<api-key>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {'X-API-Key': '<api-key>'}};
fetch('https://api.prefetch.io/search', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.prefetch.io/search",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.prefetch.io/search"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.prefetch.io/search")
.header("X-API-Key", "<api-key>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.prefetch.io/search")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["X-API-Key"] = '<api-key>'
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"query": "stripe pricing",
"type": "web",
"count": 2,
"scraped": 0,
"warnings": [],
"results": [
{
"position": 1,
"url": "https://stripe.com/pricing",
"title": "Pricing & Fees | Stripe",
"description": "Find Stripe fees and pricing information…",
"date": null,
"source": null,
"image_url": null
},
{
"position": 2,
"url": "https://docs.stripe.com/billing/subscriptions/pricing",
"title": "Pricing models | Stripe Documentation",
"description": "Learn about common pricing models…",
"date": null,
"source": null,
"image_url": null
}
]
},
"meta": {
"requestId": "6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b",
"durationMs": 1840
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}{
"error": "Credit limit exceeded",
"error_code": "CREDITS_EXHAUSTED",
"retryable": false,
"success": false,
"retryAfterSeconds": 123,
"limit": 123,
"meta": {
"requestId": "a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d",
"durationMs": 1842
}
}Overview
/search runs a web search (Google results) and returns the results best first: each one’s url, title and description (the snippet the search engine shows). Set type=news for recent articles with their publisher and date.
Ask for page content and every result page is scraped in the same request, exactly as GET /scrape would scrape it — markdown, a summary, links, structured JSON — and returned in scrape on its result. One call answers “find pages about X and give me their text”.
Use /search when you need to find pages. When you need a researched answer that cites its sources, use POST /answers; when you already know the page, use /scrape.
Example requests
curl "https://api.prefetch.io/search?query=open+source+CRM&limit=10" \
-H "X-API-Key: $PREFETCH_API_KEY"
curl "https://api.prefetch.io/search?query=stripe+pricing&limit=5&formats=markdown" \
-H "X-API-Key: $PREFETCH_API_KEY"
curl -X POST "https://api.prefetch.io/search" \
-H "X-API-Key: $PREFETCH_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "series A funding developer tools",
"type": "news",
"time_range": "week",
"limit": 5,
"scrape_options": { "formats": ["summary"] }
}'
const res = await fetch("https://api.prefetch.io/search", {
method: "POST",
headers: { "X-API-Key": process.env.PREFETCH_API_KEY, "Content-Type": "application/json" },
body: JSON.stringify({
query: "headless CMS comparison",
limit: 5,
exclude_domains: ["pinterest.com"],
scrape_options: { formats: ["markdown"], only_main_content: true },
}),
});
const { data } = await res.json();
for (const r of data.results) {
console.log(r.position, r.url, r.scrape?.markdown?.content.slice(0, 200) ?? r.scrape_error?.error);
}
Example response
{
"success": true,
"data": {
"query": "stripe pricing",
"type": "web",
"results": [
{
"position": 1,
"url": "https://stripe.com/pricing",
"title": "Pricing & Fees | Stripe",
"description": "Find Stripe fees and pricing information. Find information on pricing for payments, billing…",
"date": null,
"source": null,
"image_url": null,
"scrape": {
"url": "https://stripe.com/pricing",
"final_url": "https://stripe.com/pricing",
"domain": "stripe.com",
"status_code": 200,
"metadata": { "title": "Pricing & Fees | Stripe", "language": "en", "content_type": "text/html" },
"markdown": { "content": "# Pricing\n\nIntegrated per-transaction pricing…", "char_count": 18233, "word_count": 2611, "truncated": false, "main_content_only": true }
}
},
{
"position": 2,
"url": "https://www.g2.com/products/stripe/pricing",
"title": "Stripe Pricing 2026",
"description": "Stripe pricing and plans…",
"date": null,
"source": null,
"image_url": null,
"scrape_error": {
"error": "The target URL could not be accessed. The site may be blocking automated requests.",
"error_code": "TARGET_BLOCKED"
}
}
],
"count": 2,
"scraped": 1,
"warnings": ["1 result page(s) could not be scraped; see each result's scrape_error. They were not charged."]
},
"meta": { "requestId": "6f1d2c3b-4a5e-4f60-8b7a-9c0d1e2f3a4b", "durationMs": 8230 }
}
Narrowing a search
| Parameter | Does |
|---|---|
type | web (default) or news. News results carry source (the publisher), date and image_url. |
time_range | hour, day, week, month or year. |
country / language | Two-letter codes, e.g. country=de&language=de. |
location | A city or region to search from, e.g. Berlin, Germany. |
include_domains | Search only these domains (up to 10). |
exclude_domains | Leave these out (up to 10). |
query as they do on Google: "exact phrase", -word, filetype:pdf, site:example.com.
Results on your account’s blocklist are dropped, and the rest renumbered.
Scraping the results
Ask for content withformats on GET (formats=markdown,links, plus only_main_content and max_age), or a scrape_options object on POST — every option /scrape takes. Up to 10 results can be scraped in one request.
Result pages are fetched in parallel within one deadline (45 s by default; timeout can shorten it). A page that cannot be fetched — blocked, too slow, not a page — gets a scrape_error with an error code instead of scrape, and is not charged. The search itself still succeeds.
Credits
| What | Credits |
|---|---|
| The search | 2 per 10 results returned, rounded up — at least 2 |
| Each result page scraped | 1, or 4 when summary, json or check_prompt_injection is asked for |
| Result pages that could not be scraped | 0 |
Related
GET /scrape
POST /batch/scrape
Authorizations
Your Prefetch API key. Obtain one from the dashboard.
Query Parameters
What to search for, as you would type it into Google (1–400 characters). Search operators work ("exact phrase", -word, filetype:pdf). q is accepted too.
"best open source CRM"
Results to return (1–50; 1–10 when results are scraped). Default: 10.
"10"
web (default) or news — recent articles with their publisher and date.
web, news Two-letter country code to search from, e.g. us, de.
"us"
Two-letter language code for the results, e.g. en, de.
"en"
A city or region to search from, e.g. Berlin, Germany.
Only results from the past hour, day, week, month or year.
hour, day, week, month, year Comma-separated domains to search within (up to 10).
"github.com,gitlab.com"
Comma-separated domains to leave out (up to 10).
"pinterest.com"
Scrape each result page into these formats (the /scrape formats: markdown, summary, html, raw_html, links, images, json, screenshot). Omit to return search results only.
"markdown"
With formats: strip navigation, sidebars and footers from each page. Default: true.
With formats: accept a stored copy of a page fetched at most this many milliseconds ago.
"86400000"
Give up after this many milliseconds (5,000–45000). Result pages still loading are returned with a scrape_error and not charged.
"30000"