Issue with Inline Citations from Google Search Grounding

Hi everyone,

I want to add inline citations to model responses which are grounded to Google Search.

I rely on the explanation here:

However, the resulting indices in the grounding meta data are off and add the citation often in the next line:

Here are the results from the German 2nd Division's second matchday last weekend.

**Saturday, August 9, 2025**

*   **Fortuna Düsseldorf 0 - 2 Hannover 96**
[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==), [2](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGUwILTvRkTDxhQlrWKdfcasXNmzqgAOSKhoOBJ7B_Pfp473emZPUO2gzRYnKeMIKc7Pmu3oQPV13fda2PIJMFs99hzAxSIhQIDSN9aW5ID7o6QpOgE7IQ-REt-k4ShYuFfGjFrbD4NdpTkNy5Z)*   **Eintracht Braunschweig 3 - 2 SpVgg Greuther Fürth**
*[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==)   **Dynamo Dresden 1 - 2 1. FC Magdeburg**
*[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==)   **1. FC Kaiserslautern 1 - 0 FC Schalke 04**

[1](https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHQ_C0W-iRph_DVmOe_qRRZyx9BlJLCPiKjLTzDNriQ-zmbzQVzm0j5bNu86-wgPXkvsPqg67o-Q7k-yEdPGZeukbs3u1QdeqsFKS0Tzau-K3k8MkCi6fC1cZeGkninOP6h3Yeve9yfZe_UBgkpIPKQEJlRkTEUjXtXaiadGFhBg0E_jOv36g==)**Sunday, August 10, 2025**
[truncated]

As you see, this interrupts Markdown rendering. In some edge cases, they are placed mid-sentence.

Code example:

from google import genai
from google.genai import types
import base64


def add_citations(response):
    text = response.text
    supports = response.candidates[0].grounding_metadata.grounding_supports
    chunks = response.candidates[0].grounding_metadata.grounding_chunks

    # Sort supports by end_index in descending order to avoid shifting issues when inserting.
    sorted_supports = sorted(supports, key=lambda s: s.segment.end_index, reverse=True)

    for support in sorted_supports:
        end_index = support.segment.end_index
        if support.grounding_chunk_indices:
            # Create citation string like [1](link1)[2](link2)
            citation_links = []
            for i in support.grounding_chunk_indices:
                if i < len(chunks):
                    uri = chunks[i].web.uri
                    citation_links.append(f"[{i + 1}]({uri})")

            citation_string = ", ".join(citation_links)
            text = text[:end_index] + citation_string + text[end_index:]

    return text



def generate():
  client = genai.Client(
      vertexai=True,
      project="bah-da-llm-prod",
      location="global",
  )


  model = "gemini-2.5-pro"
  contents = [
    types.Content(
      role="user",
      parts=[
        types.Part.from_text(text="""What are the results from the German 2nd Division last weekend?""")
      ]
    ),
  ]
  tools = [
    types.Tool(google_search=types.GoogleSearch()),
  ]

  generate_content_config = types.GenerateContentConfig(
    temperature = 1,
    top_p = 0.95,
    seed = 0,
    max_output_tokens = 65535,
    safety_settings = [types.SafetySetting(
      category="HARM_CATEGORY_HATE_SPEECH",
      threshold="OFF"
    ),types.SafetySetting(
      category="HARM_CATEGORY_DANGEROUS_CONTENT",
      threshold="OFF"
    ),types.SafetySetting(
      category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
      threshold="OFF"
    ),types.SafetySetting(
      category="HARM_CATEGORY_HARASSMENT",
      threshold="OFF"
    )],
    tools = tools,
    thinking_config=types.ThinkingConfig(
      thinking_budget=-1,
    ),
  )

  response = client.models.generate_content(
    model = model,
    contents = contents,
    config = generate_content_config,
    )
  return response



response = generate()


# Assuming response with grounding metadata
text_with_citations = add_citations(response)
print(text_with_citations)

Hi @SebSchroen ,

Welcome to the Forum!!
I have run a test from my end and attached a screenshot of the result. Could you please confirm if this is the expected output?

Hi Mrinal,

thank you for your quick response. This is a similar result I see, but not the one I would expect.

Running the same prompt with the same model from the Model Garden in my Google Cloud Console gives this nice format:

Which is, what I would expect from the code and it worked fine until last week.

Hi @Mrinal_Ghosh, just following up: Could you see the difference between the two outputs?

Thank you in advance,
Sebastian

Hi @SebSchroen, I have the same issue with mid sentence placements of citations. Have you found any solution on how to solve this, or how to get the same nice result as in the Model Garden of the Google Cloud Console?

HI @Lukas_N, I wasnt able to generate the nice formatting we see in the Model Garden, just a basic footnote with a link:

Any additional formatting leads to a shift of the placements. I left it there and I assume you have to add any formatting to the rendering after adding the citations.

I still used this code snippet more or less 1:1:

1 Like

Hi @SebSchroen, thanks for your answer!
I think that is already a good solution with the links after each sentence!

On my side, I found a little different approach. For the frontend I use Angular and I always expect a markdown result from the LLM (configured in the system prompt). In Angular I render the markdwon with ngx-markdown. To get a comparable look and feel as in the model garden, I made an Angular component which is registered as an Angular Element, so that it can be used as a html element in the ngx-markdown renderer: Custom Elements • Angular
This way, I am able to render Angular Material buttons with mat-tooltips of the source inside a ngx-markdown:

The issue with the wrong “startIndex” and “endIndex” in the response from the Vertex AI API with Google Grounding I have fixed manually.
I fixed it while searching for the “text” string of the “groundingSupports”>“segment” object in the full response and setting the indices manually and ignoring the official ones returned from the endpoint as they are sometimes wrong positioned.

Here I get the “text” of the segment for which I searched in the full response to get accurate indices:
"segment": {
"startIndex": 2031,
"endIndex": 2090,
"text": "WebStorm fügt diese Flags normalerweise automatisch hinzu."
},
"groundingChunkIndices": [
2
]
},

I did that with simple string functions (here is my example in TypeScript):

genResGroundingMetadata.groundingSupports?.forEach( (support: GroundingSupport) => {
let startIndex: number = -1, endIndex: number = -1;

if (support.segment) {
// as by end of Nov 2025, the grounding indices are sometimes wrong, we search ourselves for them
startIndex = retVal.message?.indexOf(support.segment.text ?? '') ?? -1;
endIndex = startIndex + (support.segment.text ?? '').length;
}
}

With that solution I achieved a source representation as in the model garden or other AI platforms with web search capabilities.

I hope this can help you as well! Your issue here helped me a lot to get to this solution and to be really aware that there is a bug at the grounding indices returned from the API.

Best regards!