REST API
For applications, scripts, and custom agents. Send JSON with your key in a header.
https://searchpipe.tech/v1/searchAuthorization: Bearer sp-YOUR_KEY
Also accepts X-API-Key: sp-YOUR_KEY.
Developer documentation
Search the web, get cited answers, and inspect the evidence. Connect your agent through MCP or call the REST API from your application.
Create an account, verify your email, then copy your default key from API Keys. Both integration paths use the same search pipeline and credit balance. The free plan provides 1,000 credits per UTC calendar month; sign in with a verified account each month to renew the allowance.
For applications, scripts, and custom agents. Send JSON with your key in a header.
https://searchpipe.tech/v1/searchAuthorization: Bearer sp-YOUR_KEY
Also accepts X-API-Key: sp-YOUR_KEY.
For Claude Code, Cursor, Codex, and other MCP clients. Add this remote server URL.
https://searchpipe.tech/mcp/?api_key=sp-YOUR_KEYStreamable HTTP transport. Connection options →
This request returns search results and asks for a cited answer. It uses the default basic depth: 1 credit.
curl -X POST https://searchpipe.tech/v1/search \
-H "Authorization: Bearer sp-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"FastAPI tutorial","max_results":5,"output_type":"answer"}'import httpx
resp = httpx.post(
"https://searchpipe.tech/v1/search",
headers={"Authorization": "Bearer sp-YOUR_KEY"},
json={"query": "FastAPI tutorial", "max_results": 5, "output_type": "answer"},
timeout=60,
)
resp.raise_for_status()
data = resp.json()
print(data["answer"])
for r in data["results"]:
print(r["score"], r["title"], r["url"])const resp = await fetch("https://searchpipe.tech/v1/search", {
method: "POST",
headers: {
"Authorization": "Bearer sp-YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "FastAPI tutorial", max_results: 5, output_type: "answer" }),
});
if (!resp.ok) throw new Error(`Search failed (${resp.status}): ${await resp.text()}`);
const data = await resp.json();
console.log(data.answer);
for (const r of data.results) console.log(r.score, r.title, r.url);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"query": "FastAPI tutorial",
"max_results": 5,
"output_type": "answer",
})
req, _ := http.NewRequest("POST", "https://searchpipe.tech/v1/search", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer sp-YOUR_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))
}import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class SearchPipeDemo {
public static void main(String[] args) throws Exception {
String json = """
{"query":"FastAPI tutorial","max_results":5,"output_type":"answer"}
""";
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://searchpipe.tech/v1/search"))
.timeout(Duration.ofSeconds(60))
.header("Authorization", "Bearer sp-YOUR_KEY")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> resp =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body());
}
}using System.Net.Http.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "sp-YOUR_KEY");
var payload = new
{
query = "FastAPI tutorial",
max_results = 5,
output_type = "answer",
};
HttpResponseMessage resp = await client.PostAsJsonAsync("https://searchpipe.tech/v1/search", payload);
resp.EnsureSuccessStatusCode();
Console.WriteLine(await resp.Content.ReadAsStringAsync());Sign up and verify your email — a default sp- prefixed API key is generated automatically on the API Keys page of the dashboard.
POST https://searchpipe.tech/v1/search with Content-Type: application/json and authenticate with Authorization: Bearer sp-YOUR_KEY or X-API-Key: sp-YOUR_KEY.
Send query (required). max_results defaults to 5 (range 1–20). Choose fast (0.5 credits), basic (1), or advanced (2), and output_type=search_results, answer, or structured. Fast skips answer generation.
Read results and the requested answer or output. Optional generation and evidence fields can be null. Inspect warnings, notices, and usage; cache hits still cost credits.
Fix 422 validation errors before retrying. For 402, add credits; for 429, follow Retry-After. Pipeline failures and rejected output are refunded. A retry is a new request and may incur a new charge.
search_depth controls processing and cost. output_type controls what you receive alongside the result list.
| Depth | Credits / call | Behavior |
|---|---|---|
fast | 0.5 credits | Retrieval, rule fusion, and page fetching. This mode skips all LLM steps, including answers and audits. Keeps fused engine order and raw engine scores. |
basic · default | 1 credit | LLM-ranked results, with an optional cited answer. |
advanced | 2 credits | LLM-ranked results, with statement-level auditing when an answer is generated and audit data is available. |
| Value | What to read |
|---|---|
search_results · default | results; no answer generation. |
answer | results + answer, with inline [n] citations referencing zero-based result indices. |
structured | results + output, a JSON object generated using output_schema. answer is null. |
fast still returns results, but answer/output stay null. REST responses explain this in warnings.include_answer=true remains supported as a deprecated alias for output_type="answer". Explicit output_type always wins, including search_results.
Use https://searchpipe.tech/mcp/?api_key=sp-YOUR_KEY, or use https://searchpipe.tech/mcp/ with an Authorization: Bearer sp-YOUR_KEY header if your client supports it. Treat a URL containing your key as a credential.
claude mcp add --transport http searchpipe "https://searchpipe.tech/mcp/?api_key=sp-YOUR_KEY"The search tool is searchpipe_search. It accepts the search parameters below, except stream. The searchpipe_verify tool supports separate claim verification. The searchpipe_account_balance tool returns the remaining credit balance and is free to call.
| Aspect | REST API | MCP Server |
|---|---|---|
| Best for | Your application or backend | An MCP-compatible agent or client |
| Transport | HTTPS + JSON; optional SSE | Streamable HTTP (MCP) |
| Parameters | Full search request, including stream | Search parameters except stream |
| Response | Search content and request metadata | Same search content; REST-only request_id, response_time, usage, warnings, and notices remain null |
| Billing | 0.5 / 1 / 2 credits by depth | Same search costs and shared credit balance |
| Caching | Shared search cache; cache hits are charged | Same cache and billing behavior |
In API Keys, use “Copy one-line setup” to get a prompt containing your MCP link. Setup instructions are also available at /agent-setup/SKILL.md.
MCP setup & troubleshooting →Client configuration examples →
/v1/searchSend Content-Type: application/json. Only query is required. Start with the core fields; add filters and output options as needed.
| Parameter | Type / default | Description |
|---|---|---|
query | string · required— | Search question or keywords; up to 2,000 characters. |
max_results | integer5 | 1–20. This is a maximum; fewer results may match your request. |
search_depth | stringbasic | fast (0.5 credits), basic (1 credit), or advanced (2 credits). See the mode comparison above. |
output_type | stringsearch_results | search_results, answer, or structured. When explicitly set, takes priority over include_answer. |
| Parameter | Type / default | Description |
|---|---|---|
include_domains | string[] | nullnull | Up to 300 patterns. example.com includes subdomains; *.example.com matches subdomains only; example.com/docs also requires the /docs path prefix. Each pattern is limited to 253 characters. |
exclude_domains | string[] | nullnull | Up to 150 patterns, using the same matching rules. Exclusions take priority over inclusions. |
time_range | string | nullnull | day, week, month, or year. A recency hint passed to supporting search engines. |
start_date | YYYY-MM-DD | nullnull | Inclusive earliest publication date; results without a known publish date are not filtered. |
end_date | YYYY-MM-DD | nullnull | Inclusive latest publication date; results without a known publish date are not filtered. |
| Parameter | Type / default | Description |
|---|---|---|
topic | stringgeneral | general or news. news adds a news retrieval channel; it does not guarantee every result is a news article. |
language | string | nullnull (auto) | Market language, such as en; up to 5 characters. Overrides query-language detection and biases retrieval, rather than translating results. |
country | string | nullnull | Two-letter country bias, such as US. Combined with language: en + de becomes en-DE. This is not a source-country filter. |
safe_search | integer0 | 0 off, 1 moderate, 2 strict. Passed to upstream engines; coverage depends on the engine. |
| Parameter | Type / default | Description |
|---|---|---|
include_raw_content | booleanfalse | Include extracted page text in results[].raw_content when available. This is extracted text, not the original HTML. |
include_images | booleanfalse | Run an additional image search. images contains URL/description pairs and may be empty if retrieval fails. |
objective | string | nullnull | Up to 500 characters describing what matters for ranking. It does not change what the answer covers; fast skips LLM reranking. |
output_schema | object | nullnull | Required for structured output. A flat object with 1–20 primitive properties; serialized schema up to 4,000 characters. See Structured output for validation limits. |
stream | booleanfalse | REST only. Use with output_type=answer or structured, or include_answer=true. Returns Server-Sent Events. |
include_answer | booleanfalse | Deprecated alias for output_type=answer. Still supported when output_type is omitted; explicit output_type always wins. |
Versioned endpoints: POST /v1/search, POST /v1/extract, POST /v1/verify, and POST /v1/verify/batch. Existing paths /search, /extract, /verify, and /verify/batch remain valid aliases permanently, using the same handlers and billing. The account balance check GET /v1/account/balance ships under the versioned prefix only.
The result list is present in every output mode. Optional fields may be null. This illustrative basic answer response shows the envelope; values are examples, not a live search.
{
"query": "FastAPI tutorial",
"results": [{
"id": "a43f294b8c6d012e",
"url": "https://fastapi.tiangolo.com/tutorial/",
"title": "Tutorial - User Guide - FastAPI",
"content": "A step-by-step guide to using FastAPI.",
"score": 0.94,
"raw_content": null,
"published_date": null,
"evidence": null
}],
"answer": "See the FastAPI tutorial for a step-by-step guide. [0]",
"ai_generated": true,
"output": null,
"answer_audit": null,
"images": null,
"request_id": "example-request-id",
"response_time": 2.4,
"usage": {"credits_charged": 1.0, "cached": false},
"warnings": null,
"notices": null
}| Field | Meaning |
|---|---|
query / results | The query and result list. results may be empty; inspect notices and apply your own result-quality checks. |
answer / output | answer is text for answer mode; output is an object for structured mode. Either can be null when generation is unavailable. Structured mode keeps answer=null. |
ai_generated | Whether a generated answer or structured output was produced. Results-only searches can still use LLM reranking. |
request_id / response_time | REST request identifier (also X-Request-Id) and server processing time in seconds. |
usage | REST billing information: credits_charged and cached. A cache hit still costs credits. |
images | Image URL/description pairs when requested; otherwise null. Image retrieval is best effort. |
answer_audit | Statement-level report for advanced answer mode when available. Null for structured, basic, or fast output. |
warnings / notices | REST string arrays, or null. warnings explain parameters that did not take effect; notices describe cache hits or empty results. Keep warnings for developer diagnostics. |
| Field | Meaning |
|---|---|
id | Stable identifier derived from the result URL. |
url / title / content | Source URL, title, and content snippet. content may include supporting excerpts. |
score | LLM relevance score on basic/advanced; raw upstream score on fast. Fast scores are not normalized or comparable across engines. |
raw_content | Extracted page text when requested and available; otherwise null. |
published_date | Source-provided publication date, or null. Date coverage is incomplete. |
evidence | Quote, character offsets, source tier, and fetch metadata when available. May be null. |
On basic/advanced, scores reflect LLM relevance ranking. Fast uses raw engine scores, which can exceed 1 and are not comparable across engines; use the returned order instead of applying a universal score threshold.
Search results can carry evidence without a separate verification call. Advanced generated answers can also include a statement-level audit. These are two different layers:
| Layer | What it tells you | Availability |
|---|---|---|
results[].evidence | A verbatim quote, character offsets, source tier, and page-fetch metadata. | Included in search pricing when evidence is available; may be null. |
answer_audit | Checks generated answer statements against their cited sources. | Advanced depth with output_type=answer (or include_answer=true), when audit data is available. No additional audit charge. |
span_start is inclusive and span_end is exclusive. Offsets refer to extracted page text, or to content when evidence falls back to a search snippet. A snippet fallback is not a verified fetch of the original page. Request include_raw_content=true when you need the extracted text.
Source tiers are official, press, academic, preprint, community, or unknown. Fetch metadata includes fetch_status, redirected, final_url, and fetched_at. A source classification is not a truth verdict.
Audit statements include text, verdict, confidence, and citations. Verdicts are supported, partial, unsupported, or inconclusive. Statements without a valid citation marker are uncited and are not judged.
answer_audit.score is the supported fraction of judged statements. Uncited statements are excluded from that denominator; inspect uncited_count and individual statements as well. All-uncited answers score 0. Structured output does not include answer_audit.curl -X POST https://searchpipe.tech/v1/search \
-H "Authorization: Bearer sp-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "What is retrieval-augmented generation?", "output_type": "answer", "search_depth": "advanced"}'
# response.answer_audit (nullable) -> score + per-statement verdicts
# response.results[n].evidence (nullable) -> quote, span, source tierimport httpx
resp = httpx.post(
"https://searchpipe.tech/v1/search",
headers={"Authorization": "Bearer sp-YOUR_KEY"},
json={
"query": "What is retrieval-augmented generation?",
"output_type": "answer",
"search_depth": "advanced",
},
timeout=60,
)
resp.raise_for_status()
data = resp.json()
print(data["answer"])
audit = data.get("answer_audit")
print("audit score:", audit["score"] if audit else "unavailable")
for s in (audit or {}).get("statements", []):
print(s["verdict"], "-", s["text"])
for r in data["results"]:
evidence = r.get("evidence")
if evidence:
print(evidence["quote"], "[", evidence["source_tier"], "]")const resp = await fetch("https://searchpipe.tech/v1/search", {
method: "POST",
headers: {
"Authorization": "Bearer sp-YOUR_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "What is retrieval-augmented generation?",
output_type: "answer",
search_depth: "advanced",
}),
});
if (!resp.ok) throw new Error(`Search failed (${resp.status}): ${await resp.text()}`);
const data = await resp.json();
console.log(data.answer);
console.log("audit score:", data.answer_audit?.score ?? "unavailable");
for (const s of data.answer_audit?.statements ?? []) console.log(s.verdict, "-", s.text);
for (const r of data.results) {
if (r.evidence) console.log(r.evidence.quote, "[", r.evidence.source_tier, "]");
}package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
func main() {
payload, _ := json.Marshal(map[string]any{
"query": "What is retrieval-augmented generation?",
"output_type": "answer",
"search_depth": "advanced",
})
req, _ := http.NewRequest("POST", "https://searchpipe.tech/v1/search", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer sp-YOUR_KEY")
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body)) // answer_audit.statements + results[n].evidence
}import java.net.URI;
import java.net.http.*;
import java.time.Duration;
public class SearchPipeAuditedAnswer {
public static void main(String[] args) throws Exception {
String json = """
{"query":"What is retrieval-augmented generation?","output_type":"answer","search_depth":"advanced"}
""";
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://searchpipe.tech/v1/search"))
.timeout(Duration.ofSeconds(60))
.header("Authorization", "Bearer sp-YOUR_KEY")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> resp =
client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(resp.body()); // answer_audit + evidence fields
}
}using System.Net.Http.Json;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", "sp-YOUR_KEY");
var payload = new
{
query = "What is retrieval-augmented generation?",
output_type = "answer",
search_depth = "advanced",
};
HttpResponseMessage resp = await client.PostAsJsonAsync("https://searchpipe.tech/v1/search", payload);
resp.EnsureSuccessStatusCode();
Console.WriteLine(await resp.Content.ReadAsStringAsync()); // answer_audit + evidenceSet output_type="structured" and provide output_schema. SearchPipe generates a cited answer internally, then asks the model to convert it into a JSON object using your schema. Read the object from output; answer and answer_audit are null.
curl -X POST https://searchpipe.tech/v1/search \
-H "Authorization: Bearer sp-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"query": "What is FastAPI and what language is it written in?",
"output_type": "structured",
"output_schema": {
"type": "object",
"properties": {
"name": {"type": "string"},
"language": {"type": "string"}
},
"required": ["name", "language"]
}
}'Schemas must contain 1–20 flat properties of type string, number, integer, or boolean, and serialize to at most 4,000 characters. Nested objects and arrays are not supported. A missing or invalid schema returns 422.
output=null with a REST warning. Use basic or advanced; fast skips generation.REST requests with stream=true return text/event-stream. Request an answer or structured output; streaming a results-only request returns 422.
curl -N -X POST https://searchpipe.tech/v1/search \
-H "Authorization: Bearer sp-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"query":"FastAPI tutorial","output_type":"answer","stream":true}'| Event | How to use it |
|---|---|
meta | Initial request_id, query, and credits_charged. Use the final envelope for final billing. |
results | Search results and optional images, when available. Result IDs may not yet be filled. |
delta | Incremental text; zero or more events. Structured mode may stream the internal answer text, not partial JSON. |
done | Authoritative, complete SearchResponse. Read final answer/output, audit, usage, warnings, and notices here. |
error | Terminal stream failure with error.code, message, and request_id. The call is refunded; do not treat partial text as complete. |
event: meta
data: {"request_id":"example-id","query":"FastAPI tutorial","credits_charged":1.0}
event: done
data: {"query":"FastAPI tutorial","answer":null,"results":[],"ai_generated":false,"images":null,"answer_audit":null,"output":null,"request_id":"example-id","response_time":2.4,"usage":{"credits_charged":1.0,"cached":false},"warnings":["answer was requested but could not be generated (deadline or upstream failure)"],"notices":["no results matched the request"]}Example with no results: meta goes directly to done. Other requests may include results and delta events before done.
Results or deltas can be absent, including on empty results or generation failure. Cache hits still use SSE, but delta counts can differ. Handle error events and a connection closing before done, even after HTTP 200. Client disconnection does not automatically refund a request.
POST /v1/extract retrieves extracted text for 1–20 public URLs (up to 2,048 characters each). It returns results with url/raw_content, and failed_results with url/error. Individual URL failures do not fail the whole batch. Private-network URLs are blocked.
curl -X POST https://searchpipe.tech/v1/extract \
-H "Authorization: Bearer sp-YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"urls":["https://fastapi.tiangolo.com/tutorial/"]}'Default cost: 1 credit per URL. Failed URLs are refunded. Extract does not use the search-result cache or SearchResponse envelope.
For separately supplied claims and citations, use POST /v1/verify, POST /v1/verify/batch, or the MCP tool searchpipe_verify. Search evidence and advanced answer audits work within /search; they do not require this extra call.
Verification returns verdict, confidence, reason_code, and evidences. inconclusive is a valid billed outcome. Pipeline failures return 503 and are refunded.
| Field | Type | Description |
|---|---|---|
claim | string · required | The claim (statement or citation) to verify — max 2000 characters. |
mode | string · default auto | Evidence route: paper (DOI/arXiv), web, or auto — a DOI or arXiv signal picks the paper route. |
evidence_urls | string[] · optional | Up to 5 caller-supplied citations to verify; URLs are SSRF-checked. |
max_evidence | integer · default 5 | Maximum number of evidence items to return (1-10). |
supportedpartialunsupportedinconclusiveReason codes: doi_match (The claim resolved to a DOI that points to a real paper.); title_match (The claim matched a known paper title (fuzzy).); quote_span_found (A verbatim span supporting the claim was located in a fetched source.); no_supporting_evidence (Sources were reachable but none of them supports the claim.); fetch_failed (Candidate sources could not be fetched.); paywall (Candidate sources sit behind a paywall.); low_authority (Only low-authority sources were available, so confidence stays low.); conflicting_evidence (The evidence points in more than one direction.).
The Python SDK is published on PyPI: pip install searchpipe. The TypeScript SDK is not yet published on npm; its source is available in the official integration repository. You can always use the HTTP examples for the current API contract.
These examples show the existing SDK interface. Typed support can lag behind the live API; check supported fields before adopting a source package.
from searchpipe import SearchPipe
client = SearchPipe(api_key="sp-YOUR_KEY")
response = client.search("FastAPI tutorial", include_answer=True)
print(response.answer)import { SearchPipe } from "searchpipe";
const client = new SearchPipe({ apiKey: "sp-YOUR_KEY" });
const response = await client.search({ query: "FastAPI tutorial", includeAnswer: true });
console.log(response.answer);For REST API errors, inspect the HTTP status and error.code. Keep error.request_id when reporting a problem. The existing detail field is retained; for 422, it contains validation details.
{
"detail": "Insufficient credits",
"type": "error",
"error": {
"code": "insufficient_credits",
"message": "Insufficient credits",
"request_id": "example-request-id"
}
}| Status | Meaning | Action |
|---|---|---|
| 400 | Input or output content rejected | Revise the content. Do not retry the same rejected input. Rejected output is refunded. |
| 401 | Missing or invalid credentials | Check the key and authentication header. |
| 402 | Insufficient credits | Check your remaining credits with GET /v1/account/balance (free to call), then add credits before retrying. |
| 403 | Email not verified or access denied | Verify your email or check account permissions. |
| 422 | Invalid request parameters | Fix fields in detail, such as a missing output_schema or invalid streaming mode. |
| 429 | Rate limit exceeded | Respect Retry-After and reduce concurrency. |
| 502 | Search pipeline failure | Automatically refunded. Retry with backoff. |
| 503 | Service busy or verification pipeline failed | Retry with backoff. A failed pipeline call is refunded. |
A null answer/output or an empty result list can arrive with HTTP 200. Check the response before using it. Retrying is a new request and may incur a new charge.
GET /v1/account/balance (or the MCP tool searchpipe_account_balance) returns your remaining credits — not billed and not logged as usage.usage.credits_charged for the REST request.Retry-After.MCP setup guideAgent setup guideFAQSearchPipe vs Tavily vs Exa vs BraveWeb Search APIs for AI AgentsWhat is a Web Search MCP server