# Search API responses

> The JSON response of a successful PacketStream search, the result fields of each request type, rich web blocks, Markdown output, headers, and the free account endpoint.

**Early beta.** Fields, limits, and availability may change during the beta. These pages describe what the API serves today. Questions or problems? [Contact support](https://packetstream.io/contact-us/).

A successful search returns HTTP 200 with a JSON body:

```json
{
  "request_id": "6f1c2b9e-3d4a-4f7b-9c1e-2a5b8d0e7f13",
  "search": {
    "engine": "google",
    "type": "search",
    "query": "coffee grinders",
    "country": "us",
    "page": 1
  },
  "results": [
    {
      "position": 1,
      "title": "The Best Coffee Grinders of 2026",
      "url": "https://www.example.com/coffee-grinders",
      "snippet": "We tested 20 burr and blade grinders for consistency and noise.",
      "displayed_url": "https://www.example.com › reviews › coffee-grinders"
    }
  ],
  "people_also_ask": [{ "question": "Are burr grinders worth it?" }],
  "related_searches": [
    {
      "query": "best burr coffee grinder",
      "url": "https://www.google.com/search?q=best+burr+coffee+grinder"
    }
  ],
  "result_count": 1,
  "charged_units": 1,
  "balance_remaining_usd": 12.3456,
  "processing_time_ms": 2140
}
```

## Top-level fields

| Field | Description |
| --- | --- |
| `request_id` | Unique request ID, also sent as the `X-Request-Id` header. Quote it when you contact support. |
| `search` | The accepted request after normalization. `engine`, `type`, `query`, `country`, and `page` are always present. `language`, `location`, `device`, `time_range`, `safe_search`, `google_domain`, and `autocorrect` appear when you sent them. `uule` is never echoed. |
| `results` | The result list for the type, possibly empty. Absent for `ai_mode`. |
| Rich blocks | For `type=search`, any of the [web search blocks](https://packetstream.io/docs/search-api/responses/#web-search-blocks) that Google showed. |
| `ai_mode` | For `type=ai_mode`, the answer's `text_blocks` and `references`. |
| `result_count` | Items in `results`. For `ai_mode`, the number of references, which can be `0`. |
| `charged_units` | Searches billed for this response: always `1`. |
| `balance_remaining_usd` | Your balance after this search, rounded down to four decimals. May be omitted. |
| `processing_time_ms` | Server processing time in milliseconds. |

Responses may gain new optional fields and blocks over time. Ignore fields you do not recognize.

## Result items by type

Every item has a 1-based `position`. Optional fields are left out when Google does not provide them. All URLs are absolute `http` or `https` URLs: tracking redirects, relative links, and inline data are never returned.

| Type | Items per page | Item fields |
| --- | --- | --- |
| `search` | Up to 10 | `title`, `url`, `snippet`, and when available `displayed_url`, `favicon_url`, `date` (as displayed), and `sitelinks` (up to 6, each with `title`, `url`, and an optional `snippet`). |
| `news` | Up to 10 | `title`, `url`, `source`, `date` (ISO 8601 as given, or empty), and an optional `thumbnail_url`. |
| `shopping` | Up to 100 | `title`, `price` (as displayed), `source` (the merchant), and when available `extracted_price`, `old_price`, `rating`, `reviews`, `delivery`, `tag`, and `product_id`. |
| `places` | Up to 10 | `title`, `place_id`, and when available `address`, `rating`, `reviews`, `type`, `types`, `phone`, `website`, `latitude`, `longitude`, `price`, and `open_state`. |
| `autocomplete` | Up to 10 | `suggestion` and an optional `relevance`. |
| `scholar` | Up to 10 | `title`, `url`, `snippet`, `publication` (authors, venue, and year), and when available `cited_by` and `pdf_url`. |
| `ai_mode` | One answer | No `results` list. The `ai_mode` block has `text_blocks` and `references`, and `result_count` is the number of references. |

`rating` is a number from 0 to 5, and `reviews` is an integer.

## Web search blocks

`type=search` can include these blocks next to `results`. A block appears only when Google showed it and it could be read reliably, and mobile searches carry fewer blocks. Do not build logic that requires a block to be present.

| Block | Shape |
| --- | --- |
| `ai_overview` | Google's AI overview: up to 40 `text_blocks`, each with a `type` of `paragraph` or `heading` and its `text`, or `list` and its `items`. Up to 20 `references`, each with `title`, `url`, and `source`. |
| `knowledge_graph` | The knowledge panel: `title`, optional `description` and `source` (`name` and an optional `url`), up to 30 `attributes` (`name` and `value`), and up to 10 `profiles` (`name` and `url`). |
| `people_also_ask` | Up to 10 related questions, each `{"question": "…"}`. |
| `local_results` | Up to 10 map-pack places: `position`, `title`, and when available `place_id`, `rating`, `reviews`, `type`, `address`, `hours`, and `price_range`. |
| `ads` | Up to 10 text ads: `position`, `block_position` (`top` or `bottom`), `title`, `displayed_url`, and an optional `snippet`. Click-tracking links are never returned. |
| `related_searches` | Up to 10 related queries, each with `query` and its Google `url`. |
| `search_information` | When available, `total_results` (an integer), `showing_results_for` (the corrected query), and `original_query_spelling_fixed`. |

`type=ai_mode` returns an `ai_mode` block with the same shape as `ai_overview`, and no `results` list. Each text value in these blocks is at most 2,000 bytes.

Google sometimes gives an AI answer without sources. Its `references` is then an empty array, and an `ai_mode` response has `result_count: 0`. The search is still billed.

## Markdown format

Set `"format": "markdown"` to get `text/markdown; charset=utf-8` instead of JSON. The output starts with a heading that names the query, lists the results as numbered links with their snippets, then adds any AI overview or AI Mode answer, knowledge panel, places, related questions, and related searches.

Text from web pages is escaped, so the output never contains raw HTML or injected links. The format suits language-model prompts. Errors are always JSON, and batch items can only use JSON.

## Headers

| Header | Sent with | Meaning |
| --- | --- | --- |
| `X-Request-Id` | Every `/v1/` response, including errors | The request ID. |
| `X-RateLimit-Limit` | Search, batch, and fetch responses, and MCP tool calls that pass the key check | The key's burst size, `10`. |
| `X-RateLimit-Remaining` | The same responses | Requests left in the burst. Approximate, because limits are tracked on each API server. |
| `Retry-After` | `429` responses | Seconds to wait: `1`. |
| `Allow` | `405` responses | The methods the path accepts. |

## Account

`GET /v1/account` returns your balance, the price, and your usage counts. It is free and does not use a rate-limit token.

```bash
curl -s https://api.packetstream.io/v1/account \
  -H "Authorization: Bearer $PACKETSTREAM_API_KEY"
```

```json
{
  "request_id": "2c9d4e1a-7b3f-4a8e-b5c6-1d0e9f8a7b6c",
  "account": {
    "balance_usd": 12.3456,
    "price_per_1000_usd": 2.5,
    "searches_today": 42,
    "searches_last_30_days": 1830,
    "fetches_today": 7,
    "fetches_last_30_days": 120
  },
  "rate_limit": { "requests_per_second": 5, "burst": 10 }
}
```

- Counts cover accepted requests and use UTC days.
- Search counts include every request type and leave out fetches. `fetches_today` and `fetches_last_30_days` count accepted [fetch requests](https://packetstream.io/docs/fetch-api/).
- `rate_limit` is the nominal limit for each key.
