A Google AI Overviews API response has two parts: an outer envelope and a data object. First check the HTTP status. Then check the numeric status in the JSON. If both are successful, read data.ai_overview for the answer and data.sources for cited pages.

Do not assume that every successful request contains an answer or citations. Google does not show an AI Overview for every search. Your parser should accept three normal result states: no overview, an answer without sources, and an answer with sources.

The endpoint is GET /ai-overview. The request uses query, gl, and hl. See the Google AI Overviews API product page for the current request limits.

Open the API and inspect a response

What is the response shape?

A successful response observed on August 5, 2026 used this structure. The answer, screenshot URL, raw HTML, and source URL were shortened. The field names were not changed.

{
  "description": "OK",
  "status": 200,
  "data": {
    "exists": true,
    "url": "https://www.google.com/search?q=what+is+bitcoin&hl=en&gl=us&num=10",
    "ai_overview": "[answer text omitted]",
    "sources": [
      {
        "url": "https://example.com/source",
        "title": "Example source",
        "site": "example.com",
        "position": 1
      }
    ],
    "screen_url": "[screenshot URL omitted]",
    "html": "[raw HTML omitted]"
  }
}

The outer fields tell you whether the API completed its work. The fields inside data describe the captured search result. Keep those jobs separate in your code.

How should I read the envelope fields?

FieldObserved typeWhat it meansRequired check
descriptionStringA readable API result messageUse it in an error message, not as your only success test
statusNumberThe API-level status inside the JSONConfirm that it equals 200
dataObjectThe captured Google resultConfirm that it is an object before reading child fields

HTTP status and JSON status are not the same value, even when both are 200. Your HTTP client checks the first one. Your parser checks the second one.

Google AI Overviews API data fields

The table describes fields seen in the August 5 test. A field can be missing or empty in another result, so the type column says "when present" where that distinction matters.

FieldObserved typeUse in your application
data.existsBooleanProvider signal that result data exists. Do not use it alone as proof that citations exist.
data.urlStringThe Google search URL built from the request inputs
data.ai_overviewString when presentThe captured AI Overview answer
data.sourcesArray when presentThe source records connected with the answer
data.screen_urlString when presentA screenshot location for visual debugging
data.htmlString when presentThe captured page HTML, useful for debugging but often too large for a main result table

Application logic should normally use ai_overview and sources. Treat screen_url and html as debugging artifacts unless your product has a clear reason to retain them.

What is inside each source?

Each observed source was an object with a page URL, title, domain, and position.

FieldObserved typeSafe fallback
urlStringSkip the source when the URL is missing or is not a string
titleStringStore null when no title is available
siteStringStore null, or derive a hostname only after validating the URL
positionNumberStore null when it is missing or is not an integer

Keep the complete source URL. A domain is useful for grouping citations, but it cannot tell you which exact page Google connected with the answer.

Do exists, the answer, and sources mean the same thing?

No. These fields describe different parts of the result. The August test included a response with exists=true and answer text but no source records. That is why one Boolean cannot represent every useful state.

StateAnswerSourcesRecommended action
no_overviewMissing or emptyMissing or emptyRecord a completed check with no overview
answer_without_sourcesPresentMissing or emptyStore the answer and an empty source list
answer_with_sourcesPresentOne or more valid recordsStore the answer and normalized sources

The exact no-overview JSON was not retained in the August test set. The table therefore defines an application state, not a promised provider payload. Detect it from a missing or empty answer instead of inventing a required combination of exists, null, and empty arrays.

A small Python response parser

This function accepts parsed JSON and returns one predictable object. It rejects a bad envelope, removes malformed source records, and assigns one of the three states above. It does not make the HTTP request.

def parse_ai_overview_response(payload):
    if not isinstance(payload, dict):
        raise ValueError("The API response is not a JSON object.")

    if payload.get("status") != 200:
        message = payload.get("description") or "The API returned an error."
        raise ValueError(message)

    data = payload.get("data")
    if not isinstance(data, dict):
        raise ValueError("The response does not contain a data object.")

    answer_value = data.get("ai_overview")
    answer = answer_value.strip() if isinstance(answer_value, str) else None
    if not answer:
        answer = None

    raw_sources = data.get("sources")
    if not isinstance(raw_sources, list):
        raw_sources = []

    sources = []
    for source in raw_sources:
        if not isinstance(source, dict) or not isinstance(source.get("url"), str):
            continue

        position = source.get("position")
        if type(position) is not int:
            position = None

        sources.append(
            {
                "url": source["url"],
                "title": source.get("title") if isinstance(source.get("title"), str) else None,
                "site": source.get("site") if isinstance(source.get("site"), str) else None,
                "position": position,
            }
        )

    state = "no_overview"
    if answer and not sources:
        state = "answer_without_sources"
    elif answer and sources:
        state = "answer_with_sources"

    return {
        "state": state,
        "answer": answer,
        "sources": sources,
        "exists": data.get("exists") is True,
        "search_url": data.get("url") if isinstance(data.get("url"), str) else None,
        "screen_url": data.get("screen_url")
        if isinstance(data.get("screen_url"), str)
        else None,
    }

The function uses only Python's built-in data types, so you do not need another package. Keep the HTTP request on your server because browser code would expose the RapidAPI key.

Test the response fields on RapidAPI

Which errors should the client handle?

FailureWhat your code receivesWhat to do
Network failure or timeoutNo usable HTTP responseRetry a small number of times with a delay
HTTP 400A client error responseFix the request instead of repeating it unchanged
HTTP 500, 502, 503, or 504A temporary server error may have occurredRetry with a delay and a fixed attempt limit
HTTP 200 with bad JSON contractMissing data or unexpected field typesStop parsing, log a redacted sample, and alert on the contract change
HTTP 200 with no overviewNo usable answerStore no_overview as a normal result

A request without query returned HTTP 400 with The Query field is required. during the August 5 test. Repeating that request would waste another call. The same test session also saw temporary HTTP 500 responses, so a production client needs a timeout and limited retries.

What should I store?

Store the normalized result together with the request inputs and collection time. At minimum, keep the query, country, language, result state, answer, sources, and an observation timestamp in UTC.

Do not overwrite an older result. AI Overview answers and cited pages can change between requests. A history table lets you distinguish a parser bug from a real change in Google's result.

For the complete request example, read how to extract Google AI Overview answers and citations as JSON. To turn sources into a practical check, continue with the website citation checker guide.

Frequently asked questions

Is data.exists enough to detect an AI Overview?

No. Inspect the answer and source fields separately. One observed response had exists=true and answer text but no sources.

Must sources always be an array?

Your code should not assume that. Use an empty array when the field is missing or has another type, then record the state based on the usable answer and source records.

Should I store html?

Usually only for short-lived debugging or auditing. The normalized answer and source records are easier to query and use less storage.

Sources and verification dates

The response fields, source shape, HTTP 400 message, and temporary HTTP 500 behavior in this guide are observations from a live test performed on August 5, 2026. They are not guarantees about every future response.

Send a query and compare the live JSON