On this page
Overview
- Base URL
- https://api.packetstream.io
- Search API
- GET/POST /v1/search · Google results
- Fetch API
- POST /v1/fetch · page → Markdown
- MCP server
- POST /v1/mcp · Streamable HTTP
- Price
- $0.0025 per successful call ($2.50/1,000) [VERIFIED 2026-10-17]
- Failed calls
- Free (rare 500 exception)
- Balance
- Same prepaid balance as proxies; never expires
- Auth
- Authorization: Bearer ps_… (one key for all three)
- Rate limit
- 5 req/s per key, bursts to 10 (shared)
This page describes what PacketStream documents — and the docs are unusually precise about limits and error semantics — plus how the prices compare.
API key & authentication
Create the key on the Search API or MCP page of the PacketStream dashboard. It is shown once. Keys
start with ps_ followed by 64 hexadecimal characters; an account has one active key,
used by Search, Fetch and MCP. Rotating it revokes the old key immediately. The API sends no CORS headers, so it
cannot be called from a web page — keep the key on a server or in your agent’s config.
The docs load the key without writing it into shell history:
read -r -s -p 'PacketStream API key: ' PACKETSTREAM_API_KEY
printf '\n'
export PACKETSTREAM_API_KEY Check the key and balance for free (this call does not use a rate-limit token):
curl -sS https://api.packetstream.io/v1/account \
-H "Authorization: Bearer $PACKETSTREAM_API_KEY" Search API
One endpoint, /v1/search, runs a Google search and returns up to 10 web results (position, title,
URL, snippet) plus any rich blocks Google showed, such as an AI overview. POST is preferred; GET takes the same
fields as query parameters but puts the query in URLs that proxies and logs may record.
curl -s https://api.packetstream.io/v1/search \
-H "Authorization: Bearer $PACKETSTREAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "coffee grinders", "country": "us"}' import os
import requests
response = requests.post(
"https://api.packetstream.io/v1/search",
headers={"Authorization": f"Bearer {os.environ['PACKETSTREAM_API_KEY']}"},
json={"query": "coffee grinders", "type": "news"},
timeout=35,
)
response.raise_for_status()
for result in response.json()["results"]:
print(result["position"], result["title"], result["url"]) const response = await fetch("https://api.packetstream.io/v1/search", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PACKETSTREAM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ query: "coffee grinders", type: "news" }),
signal: AbortSignal.timeout(35_000),
});
const payload = await response.json();
if (!response.ok) throw new Error(`${payload.error.code}: ${payload.error.message}`);
console.log(payload.result_count, payload.results[0]); Request types
type | Returns | Options |
|---|---|---|
search (default) | Web results + rich blocks | All options |
news | Google News articles | country, language, page |
shopping | Google Shopping offers | country, language, page |
places | Google Maps places | country, language |
autocomplete | Query suggestions | country, language |
scholar | Google Scholar publications | language, page |
ai_mode | A Google AI Mode answer with references | country, language |
Parameters
- query
- required · 1–400 bytes (200 for autocomplete) · operators like site: pass through
- type
- search (default) · see table
- country
- us (default) · ISO 3166-1 alpha-2
- language
- e.g. en, de, pt-BR
- location / uule
- Google location name or token (not both)
- page
- 1–10
- device
- desktop (default) or mobile
- time_range
- hour, day, week, month, year
- safe_search
- false (default)
- google_domain
- e.g. google.co.uk
- autocorrect
- true (default)
- format
- json (default) or markdown
The API never drops an option silently: an option a type does not accept returns 400 unsupported_option
before any charge. Unknown or mistyped fields return 400 invalid_request, naming the field.
curl -s https://api.packetstream.io/v1/search \
-H "Authorization: Bearer $PACKETSTREAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "espresso machines", "type": "shopping", "country": "gb", "page": 2}' The same search as a GET:
curl -s -G https://api.packetstream.io/v1/search \
-H "Authorization: Bearer $PACKETSTREAM_API_KEY" \
--data-urlencode "query=espresso machines" \
--data-urlencode "type=shopping" \
--data-urlencode "country=gb" \
--data-urlencode "page=2" Batch searches
POST /v1/search/batch runs 1–10 searches in one request (JSON only, body up to 16 KiB). Each item is
validated, rate-limited and billed like a single search; a valid batch returns HTTP 200 even if some items fail,
so check each entry’s status. A 10-item batch uses a key’s entire burst allowance.
curl -s https://api.packetstream.io/v1/search/batch \
-H "Authorization: Bearer $PACKETSTREAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"searches": [
{"query": "coffee grinders"},
{"query": "coffee grinders", "type": "shopping"},
{"query": "coffee grinders", "type": "news", "device": "mobile"}
]}' Billing: $0.0025 per successful search, every type and option. A search that runs but finds no results is billed. Query text is not stored; PacketStream records request ID, type, outcome, latency and charge.
Fetch API
POST /v1/fetch reads one public page live through the residential network and returns
its main content as Markdown, with the final URL, HTTP status, title and description. It accepts POST only, so URLs
stay out of query strings.
curl -s https://api.packetstream.io/v1/fetch \
-H "Authorization: Bearer $PACKETSTREAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://go.dev/doc/effective_go", "max_length": 20000}' import os
import requests
response = requests.post(
"https://api.packetstream.io/v1/fetch",
headers={"Authorization": f"Bearer {os.environ['PACKETSTREAM_API_KEY']}"},
json={"url": "https://go.dev/doc/effective_go", "max_length": 20000},
timeout=35,
)
response.raise_for_status()
page = response.json()["page"]
print(page["title"], page["final_url"])
print(page["content"])
if page["truncated"]:
print("Continue with start_index", page["next_start_index"]) const response = await fetch("https://api.packetstream.io/v1/fetch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.PACKETSTREAM_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://go.dev/doc/effective_go" }),
signal: AbortSignal.timeout(35_000),
});
const payload = await response.json();
if (!response.ok) throw new Error(`${payload.error.code}: ${payload.error.message}`);
console.log(payload.page.title, payload.page.content); The documented response for the curl example, with the page text shortened:
{
"request_id": "0b9e6c1d-2f4a-4b8c-9d3e-5f6a7b8c9d0e",
"fetch": {
"url": "https://go.dev/doc/effective_go",
"content": "main",
"include_links": true,
"country": "us"
},
"page": {
"final_url": "https://go.dev/doc/effective_go",
"status_code": 200,
"title": "Effective Go - The Go Programming Language",
"description": "",
"content_type": "text/markdown",
"source_content_type": "text/html",
"fetched_at": "2026-10-07T12:00:00Z",
"content": "# Effective Go\n\n## Introduction\n\nGo is a new language. …",
"start_index": 0,
"returned_length": 20000,
"total_length": 104112,
"truncated": true,
"next_start_index": 20000,
"source_truncated": false
},
"charged_units": 1,
"balance_remaining_usd": 12.3475,
"processing_time_ms": 910
} Request fields
- url
- required · public http/https, ports 80/443, ≤ 2,048 bytes
- content
- main (default, readability extraction) or full
- include_links
- true (default); false drops images and link URLs
- max_length
- 50,000 default · 1–500,000 characters
- start_index
- 0 default · for reading long pages in parts
- country
- us (default) · ISO code, e.g. gb not uk
- user_agent
- current desktop Chrome (default) · ≤ 512 chars
How it behaves
- Content types. Fetch reads the HTML and text the server returns. Pages that need JavaScript return
422 javascript_required, and PDFs and images returnunsupported_content_type; both are free. - Retries. One automatic retry after a network or connection failure.
- Plain request. One GET over HTTP/1.1 with a browser-style Accept,
Accept-Language: en-US, no cookies, no Referer. - Redirects. Follows 301/302/303/307/308 up to 5; a sixth returns
too_many_redirects. - Public URLs only. IP addresses, internal hosts, URLs with credentials or containing an API key are refused.
- Time. Server gives up after 25 seconds; set your client timeout to at least 35.
- robots.txt. Fetch does not read it; you need whatever permission the law, a contract or the site operator requires.
- Privacy. Records request ID, outcome, latency and charge — not the URL, user agent or page content.
Long pages: when page.truncated is true, send the same request with start_index set to
page.next_start_index. Each part downloads the page again and is billed again. Billing:
$0.0025 per fetch that returns content; every error is free, including the target’s own 4xx/5xx,
timeouts and rate limits.
MCP server
The MCP server at https://api.packetstream.io/v1/mcp exposes two tools to AI agents. It uses the
Streamable HTTP transport and is stateless: each POST gets one JSON response, with no event stream
and no session. Authentication is an Authorization: Bearer header — no OAuth — so your client must
let you set a custom header.
| Tool | Does | Differs from REST | Price |
|---|---|---|---|
packetstream_search | Google search; Markdown text plus JSON as structuredContent | No format argument | $0.0025 |
packetstream_fetch | Reads a public page as Markdown | Always US exit (no country); include_links false; max_length 40,000 (1,000–100,000) | $0.0025 |
-
Listing tools,
initializeandpingare free and need no key — so a client showing the server as “connected” has not checked your key. Use the free account check above. -
Fetched text is wrapped in
<<<PAGE CONTENT: untrusted>>>markers; agents should treat it as data, never instructions. -
Tools were renamed from
web_search/web_fetchin version 1.3.0; update any allow-lists that name the old tools.
The server’s instructions ask agents to use these tools for every web search and page fetch, in place of their built-in web tools, so one agent task can make many billed calls. PacketStream’s docs say to review your client’s tool-approval settings to control spend. At $0.0025 a call, 1,000 calls is $2.50.
MCP client setup
Configs below are verbatim from PacketStream’s MCP client
setup page. Replace YOUR_API_KEY with your key. PacketStream says it tested Claude Code
2.1.284, Codex CLI 0.159.0 and the mcp-remote 0.14.2 bridge against the live server on 7 October 2026; the
Cursor, VS Code and Claude Desktop steps follow those vendors’ own docs.
Claude Code
export PACKETSTREAM_API_KEY="YOUR_API_KEY"
claude mcp add --transport http --scope user packetstream https://api.packetstream.io/v1/mcp \
--header 'Authorization: Bearer ${PACKETSTREAM_API_KEY}'
claude mcp list To share with a team, commit a .mcp.json at the repository root; each person sets their own variable:
{
"mcpServers": {
"packetstream": {
"type": "http",
"url": "https://api.packetstream.io/v1/mcp",
"headers": { "Authorization": "Bearer ${PACKETSTREAM_API_KEY}" }
}
}
} Codex CLI
export PACKETSTREAM_API_KEY="YOUR_API_KEY"
codex mcp add packetstream --url https://api.packetstream.io/v1/mcp \
--bearer-token-env-var PACKETSTREAM_API_KEY [mcp_servers.packetstream]
url = "https://api.packetstream.io/v1/mcp"
bearer_token_env_var = "PACKETSTREAM_API_KEY" Cursor
{
"mcpServers": {
"packetstream": {
"url": "https://api.packetstream.io/v1/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
} VS Code
{
"inputs": [
{
"type": "promptString",
"id": "packetstream-api-key",
"description": "PacketStream API key",
"password": true
}
],
"servers": {
"packetstream": {
"type": "http",
"url": "https://api.packetstream.io/v1/mcp",
"headers": { "Authorization": "Bearer ${input:packetstream-api-key}" }
}
}
} Claude Desktop
{
"mcpServers": {
"packetstream": {
"command": "npx",
"args": [
"-y",
"mcp-remote",
"https://api.packetstream.io/v1/mcp",
"--transport",
"http-only",
"--header",
"Authorization:${PACKETSTREAM_AUTH_HEADER}"
],
"env": { "PACKETSTREAM_AUTH_HEADER": "Bearer YOUR_API_KEY" }
}
}
} Other clients
{
"mcpServers": {
"packetstream": {
"type": "http",
"url": "https://api.packetstream.io/v1/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Clients that cannot send a custom header — ChatGPT on the web, and Claude custom connectors without Anthropic’s
request-header beta — can list the tools but every call returns missing_api_key. Key errors
(missing_api_key, invalid_api_key, unauthorized) are never billed.
Rate limits, errors & retries
- Rate limit
- 5 req/s per key, bursts of 10 — Search, batch items, Fetch and MCP share it
- Over the limit
- 429 rate_limited, Retry-After: 1
- Fetch concurrency
- 4 per key per API server (429 concurrency_limited)
- Per-site fetch limit
- “a few” new fetches/s per domain, across all customers
- Server timeout
- 25 s (client ≥ 35 s)
- Request body
- ≤ 4,096 bytes (batch ≤ 16 KiB)
- Minimum balance
- $0.0025 before a call starts
The docs’ retry rules are sensible defaults: retry a 429 after Retry-After; retry a
503 with exponential backoff and jitter; retry a 500 at most once (it can rarely follow
a successful charge — check your balance first if a duplicate matters); never retry a 400, 401, 402, 405 or 413
without changing something. If your client times out before the server’s 25 seconds, the search can still finish
and be billed.
The per-site fetch limit is shared across all PacketStream customers. For fast crawls of a single domain, run your own crawler through the $1/GB proxies.
Pricing compared
| Service | Per 1,000 | Billing model |
|---|---|---|
| PacketStream Search API | $2.50 | Prepaid, never expires; no subscription; failures free |
| Bright Data SERP API | $1.50 | Pay-as-you-go, success-only; 5K free records/month |
| Oxylabs Web API (Google) | $1.00 → $0.50 | Subscription only |
| Apify Google SERP proxy | $2.50 → $1.70 | Platform plans |
| ScraperAPI Hobby | ≈ $12.25 | Subscription; Google = 25 credits |
The Search API costs about a fifth of ScraperAPI’s Hobby rate for Google and matches Apify’s entry SERP price, with no subscription. Searches come out of the same never-expiring balance as the proxies, failed calls are free, its terms describe no up-front ID check, and one MCP server covers search and fetch. Fetch costs the same $0.0025 per page that returns content, with clean Markdown out and every error free. Billing and SERP maths are worked through on the pricing page.
Strengths of the APIs
@@ strengths @@
- Searches and fetches use the same never-expiring balance as the proxies; no subscription
- Failed calls are free, including the target site’s own errors (rare 500 exception)
- Seven Google result types, including AI Mode and Scholar
- Clean Markdown output, with the final URL, status, title and description
- One MCP server for search and fetch, with documented configs for five clients
- No up-front ID check described in the terms
- Precise docs on errors, billing edge cases and retries
If you already hold a PacketStream balance, the APIs are a low-commitment way to give an agent Google search and page reads: no plan, failed calls free, and the same balance as the proxies.