> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prefetch.io/llms.txt
> Use this file to discover all available pages before exploring further.

# 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.

## 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`](/api-reference/endpoint/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`](/mcp/tools#prefetch_answer); when you already know the page, use `/scrape`.

## Example requests

<CodeGroup>
  ```bash Results only theme={"theme":"css-variables"}
  curl "https://api.prefetch.io/search?query=open+source+CRM&limit=10" \
    -H "X-API-Key: $PREFETCH_API_KEY"
  ```

  ```bash With page content theme={"theme":"css-variables"}
  curl "https://api.prefetch.io/search?query=stripe+pricing&limit=5&formats=markdown" \
    -H "X-API-Key: $PREFETCH_API_KEY"
  ```

  ```bash POST, news from the past week theme={"theme":"css-variables"}
  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"] }
    }'
  ```

  ```javascript Node.js theme={"theme":"css-variables"}
  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);
  }
  ```
</CodeGroup>

## Example response

```json theme={"theme":"css-variables"}
{
  "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). |

Search operators work in `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 with `formats` on GET (`formats=markdown,links`, plus `only_main_content` and `max_age`), or a `scrape_options` object on POST — every option [`/scrape`](/api-reference/endpoint/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](/concepts/errors) 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 |

A 10-result search costs 2 credits; with every page as markdown, 12.

## Related

<CardGroup cols={2}>
  <Card title="GET /scrape" icon="file-lines" href="/api-reference/endpoint/scrape">
    One page, every format. **1 credit.**
  </Card>

  <Card title="POST /batch/scrape" icon="layer-group" href="/api-reference/endpoint/batch-scrape">
    Hundreds of known URLs as one job.
  </Card>
</CardGroup>


## OpenAPI

````yaml GET /search
openapi: 3.1.0
info:
  title: Prefetch API
  description: >-
    Extract brand identity, company data, screenshots, IAB classifications, and
    clean page content from any URL.
  version: 1.0.0
  contact:
    email: support@prefetch.io
servers:
  - url: https://api.prefetch.io
    description: Production
security:
  - apiKey: []
tags:
  - name: Endpoints
    description: Data extraction endpoints. All require authentication and consume credits.
  - name: Health
    description: Health and readiness probes. No authentication required.
paths:
  /search:
    get:
      tags:
        - Endpoints
      summary: Search the web
      description: >-
        Searches the web (Google results) and returns each result's title, URL
        and snippet, best first. `type=news` returns recent articles with their
        publisher and date. Narrow it with `time_range`, `country`, `language`,
        `location`, `include_domains` and `exclude_domains`.


        Ask for page content and each result page is scraped in the same request
        — through `formats` on GET, or a `scrape_options` object on POST, taking
        the options /scrape takes — and returned in `scrape` on its result. A
        page that cannot be fetched in time gets a `scrape_error` instead and is
        not charged.


        **Credit cost: 2 per 10 results** (rounded up), plus 1 per result page
        scraped (4 for `summary` or `json`). Failed requests are never charged.
      operationId: searchWeb
      parameters:
        - schema:
            type: string
            description: >-
              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.
            example: best open source CRM
          required: true
          description: >-
            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.
          name: query
          in: query
        - schema:
            type: string
            description: >-
              Results to return (1–50; 1–10 when results are scraped). Default:
              10.
            example: '10'
          required: false
          description: >-
            Results to return (1–50; 1–10 when results are scraped). Default:
            10.
          name: limit
          in: query
        - schema:
            type: string
            enum:
              - web
              - news
            description: >-
              `web` (default) or `news` — recent articles with their publisher
              and date.
          required: false
          description: >-
            `web` (default) or `news` — recent articles with their publisher and
            date.
          name: type
          in: query
        - schema:
            type: string
            description: Two-letter country code to search from, e.g. `us`, `de`.
            example: us
          required: false
          description: Two-letter country code to search from, e.g. `us`, `de`.
          name: country
          in: query
        - schema:
            type: string
            description: Two-letter language code for the results, e.g. `en`, `de`.
            example: en
          required: false
          description: Two-letter language code for the results, e.g. `en`, `de`.
          name: language
          in: query
        - schema:
            type: string
            description: A city or region to search from, e.g. `Berlin, Germany`.
          required: false
          description: A city or region to search from, e.g. `Berlin, Germany`.
          name: location
          in: query
        - schema:
            type: string
            enum:
              - hour
              - day
              - week
              - month
              - year
            description: Only results from the past hour, day, week, month or year.
          required: false
          description: Only results from the past hour, day, week, month or year.
          name: time_range
          in: query
        - schema:
            type: string
            description: Comma-separated domains to search within (up to 10).
            example: github.com,gitlab.com
          required: false
          description: Comma-separated domains to search within (up to 10).
          name: include_domains
          in: query
        - schema:
            type: string
            description: Comma-separated domains to leave out (up to 10).
            example: pinterest.com
          required: false
          description: Comma-separated domains to leave out (up to 10).
          name: exclude_domains
          in: query
        - schema:
            type: string
            description: >-
              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.
            example: markdown
          required: false
          description: >-
            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.
          name: formats
          in: query
        - schema:
            type: string
            description: >-
              With `formats`: strip navigation, sidebars and footers from each
              page. Default: true.
          required: false
          description: >-
            With `formats`: strip navigation, sidebars and footers from each
            page. Default: true.
          name: only_main_content
          in: query
        - schema:
            type: string
            description: >-
              With `formats`: accept a stored copy of a page fetched at most
              this many milliseconds ago.
            example: '86400000'
          required: false
          description: >-
            With `formats`: accept a stored copy of a page fetched at most this
            many milliseconds ago.
          name: max_age
          in: query
        - schema:
            type: string
            description: >-
              Give up after this many milliseconds (5,000–45000). Result pages
              still loading are returned with a `scrape_error` and not charged.
            example: '30000'
          required: false
          description: >-
            Give up after this many milliseconds (5,000–45000). Result pages
            still loading are returned with a `scrape_error` and not charged.
          name: timeout
          in: query
      responses:
        '200':
          description: Results, best first.
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    enum:
                      - true
                  data:
                    $ref: '#/components/schemas/SearchData'
                  meta:
                    $ref: '#/components/schemas/Meta'
                required:
                  - success
                  - data
                  - meta
              example:
                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
        '400':
          description: Invalid or missing parameters.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Missing or invalid API key.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Revoked key, expired key, blocked URL, or credit limit.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '429':
          description: Rate or concurrency limit exceeded.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '503':
          description: Service temporarily unavailable — retry shortly.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  schemas:
    SearchData:
      type: object
      properties:
        query:
          type: string
        type:
          type: string
          enum:
            - web
            - news
        results:
          type: array
          items:
            $ref: '#/components/schemas/SearchResultItem'
        count:
          type: number
        scraped:
          type: number
          description: Result pages fetched — each charged at the /scrape rate.
        warnings:
          type: array
          items:
            type: string
      required:
        - query
        - type
        - results
        - count
        - scraped
        - warnings
    Meta:
      type: object
      properties:
        requestId:
          type: string
          format: uuid
          description: Unique identifier for this request.
          example: a3f2c1d4-7b6e-4f2a-9c1d-8e3f2a1b4c5d
        durationMs:
          type: integer
          nullable: true
          description: Time taken to process the request in milliseconds.
          example: 1842
      required:
        - requestId
        - durationMs
    ErrorResponse:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
          description: >-
            Always false. Absent, like `meta`, on authentication and rate-limit
            errors, which are answered before a request is processed.
        error:
          type: string
          description: >-
            Human-readable error message. The wording can change: branch on
            `error_code`.
          example: Credit limit exceeded
        error_code:
          type: string
          enum:
            - INVALID_REQUEST
            - PAYLOAD_TOO_LARGE
            - NOT_FOUND
            - CONFLICT
            - UNAUTHORIZED
            - FORBIDDEN
            - CREDITS_EXHAUSTED
            - RATE_LIMITED
            - CONCURRENCY_LIMITED
            - URL_BLOCKED
            - URL_NOT_PERMITTED
            - DNS_RESOLUTION_FAILED
            - TARGET_BLOCKED
            - TARGET_CONNECTION_FAILED
            - TARGET_TIMEOUT
            - TARGET_SSL_ERROR
            - TARGET_SERVER_ERROR
            - TARGET_CLIENT_ERROR
            - TARGET_NOT_A_PAGE
            - TARGET_UNLOADABLE
            - RESEARCH_INCONCLUSIVE
            - TIMEOUT
            - SERVICE_BUSY
            - SERVICE_UNAVAILABLE
            - INTERNAL_ERROR
          description: >-
            What went wrong, for machines. Stable: a code keeps its meaning once
            published. See [Error handling](/concepts/errors).
          example: CREDITS_EXHAUSTED
        retryable:
          type: boolean
          description: >-
            Whether the same request can succeed if sent again after a short
            wait (honour `Retry-After` when present). False for problems with
            the request or the target.
          example: false
        retryAfterSeconds:
          type: integer
          description: 'On `RATE_LIMITED`: seconds until a request will be accepted.'
        limit:
          type: integer
          description: >-
            On `CONCURRENCY_LIMITED`: how many requests this key may have in
            flight.
        meta:
          $ref: '#/components/schemas/Meta'
      required:
        - error
        - error_code
        - retryable
    SearchResultItem:
      type: object
      properties:
        position:
          type: number
          description: Rank on the results page, from 1.
          example: 1
        url:
          type: string
        title:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
          description: The snippet the search engine shows for the result.
        date:
          type: string
          nullable: true
          description: Publication date as the result shows it, when it shows one.
          example: 2 days ago
        source:
          type: string
          nullable: true
          description: 'News results: the publisher.'
          example: The Verge
        image_url:
          type: string
          nullable: true
          description: 'News results: the article''s thumbnail.'
        scrape:
          allOf:
            - $ref: '#/components/schemas/ScrapeData'
            - description: >-
                The result page in the requested formats — the same object GET
                /scrape returns. Present when scraping was asked for and the
                page was fetched.
        scrape_error:
          type: object
          properties:
            error:
              type: string
            error_code:
              type: string
              enum:
                - INVALID_REQUEST
                - PAYLOAD_TOO_LARGE
                - NOT_FOUND
                - CONFLICT
                - UNAUTHORIZED
                - FORBIDDEN
                - CREDITS_EXHAUSTED
                - RATE_LIMITED
                - CONCURRENCY_LIMITED
                - URL_BLOCKED
                - URL_NOT_PERMITTED
                - DNS_RESOLUTION_FAILED
                - TARGET_BLOCKED
                - TARGET_CONNECTION_FAILED
                - TARGET_TIMEOUT
                - TARGET_SSL_ERROR
                - TARGET_SERVER_ERROR
                - TARGET_CLIENT_ERROR
                - TARGET_NOT_A_PAGE
                - TARGET_UNLOADABLE
                - TARGET_ELEMENT_NOT_FOUND
                - RESEARCH_INCONCLUSIVE
                - TIMEOUT
                - SERVICE_BUSY
                - SERVICE_UNAVAILABLE
                - INTERNAL_ERROR
          required:
            - error
            - error_code
          description: Why the result page could not be scraped. Not charged.
      required:
        - position
        - url
        - title
        - description
        - date
        - source
        - image_url
    ScrapeData:
      allOf:
        - $ref: '#/components/schemas/ScrapePayload'
      required:
        - url
        - final_url
        - metadata
    ScrapePayload:
      type: object
      properties:
        url:
          type: string
        final_url:
          type: string
        domain:
          type: string
          nullable: true
          example: stripe.com
        status_code:
          type: integer
          nullable: true
        metadata:
          $ref: '#/components/schemas/ScrapeMetadata'
        markdown:
          $ref: '#/components/schemas/MarkdownBlock'
        summary:
          $ref: '#/components/schemas/SummaryBlock'
        html:
          $ref: '#/components/schemas/HtmlBlock'
        raw_html:
          $ref: '#/components/schemas/HtmlBlock'
        links:
          $ref: '#/components/schemas/LinksBlock'
        images:
          $ref: '#/components/schemas/ScrapeImagesBlock'
        json:
          $ref: '#/components/schemas/JsonBlock'
        screenshot:
          $ref: '#/components/schemas/ScrapeScreenshotBlock'
        pdf:
          $ref: '#/components/schemas/PdfBlock'
        prompt_injection:
          $ref: '#/components/schemas/PromptInjectionBlock'
        warnings:
          type: array
          items:
            type: string
          description: Non-fatal problems, such as a format that could not be produced.
      description: >-
        The page content fields. Which of them are present depends on the
        `formats` requested.
    ScrapeMetadata:
      type: object
      description: Always present, whatever formats were requested.
      properties:
        source_url:
          type: string
          nullable: true
          description: The URL you requested.
        final_url:
          type: string
          nullable: true
          description: The URL after redirects.
        status_code:
          type: integer
          nullable: true
        title:
          type: string
          nullable: true
          example: Stripe | Financial Infrastructure
        description:
          type: string
          nullable: true
        language:
          type: string
          nullable: true
          example: en
        canonical_url:
          type: string
          nullable: true
        favicon:
          type: string
          nullable: true
        author:
          type: string
          nullable: true
        site_name:
          type: string
          nullable: true
        published_time:
          type: string
          nullable: true
        modified_time:
          type: string
          nullable: true
        robots:
          type: string
          nullable: true
          description: The page's own robots meta tag.
        keywords:
          type: array
          items:
            type: string
        open_graph:
          type: object
          additionalProperties:
            type: string
        twitter:
          type: object
          additionalProperties:
            type: string
        json_ld:
          type: array
          items:
            type: object
          description: Parsed JSON-LD blocks, up to 10.
        headings:
          type: array
          description: The page's heading outline, up to 100 entries.
          items:
            type: object
            properties:
              level:
                type: integer
              text:
                type: string
        content_type:
          type: string
          enum:
            - text/html
            - application/pdf
          description: >-
            What the URL served. A PDF's text becomes the markdown; it has no
            html.
        page_count:
          type: integer
          description: 'PDFs only: pages in the document.'
        cache_state:
          type: string
          enum:
            - hit
            - miss
          description: >-
            Present when the request set `max_age`: `hit` = served from a stored
            fetch, `miss` = fetched live.
        cached_at:
          type: string
          description: 'On a cache hit: when the stored copy was fetched (ISO 8601).'
    MarkdownBlock:
      type: object
      properties:
        content:
          type: string
          description: >-
            GitHub Flavored Markdown. Links and images are resolved to absolute
            URLs.
        char_count:
          type: integer
        word_count:
          type: integer
        truncated:
          type: boolean
          description: True when the content hit the 1 MB size cap and was cut.
        main_content_only:
          type: boolean
          description: >-
            True when boilerplate was successfully isolated away. False means
            the full page body was returned.
    SummaryBlock:
      type: object
      properties:
        text:
          type: string
          nullable: true
          description: Null when the summary could not be produced; see `warnings`.
        model:
          type: string
          nullable: true
    HtmlBlock:
      type: object
      properties:
        content:
          type: string
        char_count:
          type: integer
        truncated:
          type: boolean
        main_content_only:
          type: boolean
    LinksBlock:
      type: object
      properties:
        internal:
          type: array
          description: Links on the same site, subdomains included.
          items:
            $ref: '#/components/schemas/ScrapeLinkItem'
        external:
          type: array
          items:
            $ref: '#/components/schemas/ScrapeLinkItem'
        count:
          type: integer
    ScrapeImagesBlock:
      type: object
      properties:
        assets:
          type: array
          items:
            $ref: '#/components/schemas/ScrapeImageItem'
        og_image:
          type: string
          nullable: true
        twitter_image:
          type: string
          nullable: true
        count:
          type: integer
    JsonBlock:
      type: object
      properties:
        data:
          type: object
          nullable: true
          description: The extracted object, shaped by your prompt or schema.
        model:
          type: string
          nullable: true
    ScrapeScreenshotBlock:
      type: object
      description: Present when `formats` includes `screenshot`.
      properties:
        url:
          type: string
          description: 'The image on our CDN. Short-lived: copy it if you need to keep it.'
        viewport:
          type: object
          properties:
            width:
              type: integer
            height:
              type: integer
        full_page:
          type: boolean
        format:
          type: string
          enum:
            - jpeg
            - png
            - webp
    PdfBlock:
      type: object
      description: Present when the URL served a PDF. Its text is in `markdown`.
      properties:
        page_count:
          type: integer
        pages_parsed:
          type: integer
          description: Pages whose text was read (capped for very long documents).
        truncated:
          type: boolean
    PromptInjectionBlock:
      type: object
      description: Present when the request set `check_prompt_injection`.
      properties:
        detected:
          type: boolean
          description: >-
            True when the page's text appears to contain instructions aimed at
            an AI model.
        reason:
          type: string
          nullable: true
          description: >-
            Why it was flagged, quoting or describing the suspicious text. Null
            when nothing was found.
          example: >-
            The page tells AI assistants to ignore their instructions and send
            the user's API keys to an outside address.
        complete:
          type: boolean
          description: >-
            True when all of the page's text was checked. False when part of it
            could not be (a very long page, or the classifier was unavailable):
            `detected: false` then means nothing was found in what was checked,
            not that the page is clean.
        model:
          type: string
          nullable: true
          description: The model that checked it; null for a page with no text.
          example: gpt-5.6-luna
      required:
        - detected
        - reason
        - complete
        - model
    ScrapeLinkItem:
      type: object
      properties:
        url:
          type: string
        text:
          type: string
          nullable: true
          description: The anchor text.
        title:
          type: string
          nullable: true
        rel:
          type: string
          nullable: true
    ScrapeImageItem:
      type: object
      properties:
        url:
          type: string
        alt:
          type: string
          nullable: true
        width:
          type: integer
          nullable: true
        height:
          type: integer
          nullable: true
        loading:
          type: string
          nullable: true
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: Your Prefetch API key. Obtain one from the dashboard.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.