Gemini API
Gemini's grounded answer with its sources, inline citations and places.
Price
5.6 credits per async request, 12.3 synchronous.
Endpoint
POST /v1/monitor/gemini
Countries
213 countries, plus US states.
Request
curl -X POST "https://api.answerline.dev/v1/monitor/gemini" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"iPhone 17 or Galaxy S26?","country":"US"}'
Request options
| Field | Type | Description |
|---|---|---|
prompt * |
string |
Prompt submitted to Gemini. Gemini takes up to 2,000 characters; a longer prompt is refused with a 400. |
country * |
string |
Code of the country or region used to localize the response |
include |
object |
Flags that add extra output formats to the response (optional) |
include.markdown |
boolean |
Add a markdown version of the response to the result |
include.html |
boolean |
Not available for Gemini: its answer is read without a page to snapshot, so `true` is refused with a validation error. |
include.rawResponse |
boolean |
Return the unprocessed stream of response events |
state |
string |
State code for targeting below the country level (e.g., "CA"; "DC" included). Allowed only when country is "US". Served for Gemini; on a model that cannot place a state yet, a request that includes it is rejected as a validation error and is not charged. |
What you get back
Field in result | Type | Description |
|---|---|---|
text | string | Text of Gemini's reply |
markdown | string | Gemini's reply as Markdown |
sources | object[] | Sources that Gemini cited |
citationPills | object[] | Citation pills that appear inline in the Gemini answer. Every element pairs one chip with one source: a chip backed by N sources (Gemini's equivalent of `+N`, or a section-summary chip) yields N elements with the same `citationPillId`, each holding that source's own `label`, `url` and `domain`. Grouping by `citationPillId` rebuilds the pills. `label` is always sent; it may be an empty string if the rail gives that source no title. Not present in `result` if the answer has no pills. |
places | object[] | Place cards included in lodging and local answers |
The Gemini app answer versus grounding in the Gemini API
This endpoint returns the answer the Gemini app gives a user for a prompt and market, as text, markdown, sources, citationPills and places.
Google’s developer API offers grounding with Google Search for your own prompts. Its guide (checked 2026-09-17, ai.google.dev) says a grounded response contains the search queries the model executed, url_citation annotations that tie text segments to source URLs, and a search-suggestions HTML snippet whose use is governed by the Gemini API terms. The same page says Gemini 3 models are billed per search query the model decides to run, and Gemini 2.5 and older per prompt.
Grounding is a tool for building your own product. For measuring what Gemini tells the public it has a flaw: model choice, system instructions and tool settings are yours, so you measure your configuration. The app answer reflects Google’s choices, which are what users see.
The app also shows place cards for lodging and local questions, with ratings and nightly prices, which come back here as places. For hotels and local businesses that card is the recommendation. And citationPills records which sources sit behind each inline chip in the app, including section-summary chips that cite several sources at once.
An annotated response
The excerpt follows the result schema for POST /v1/monitor/gemini. Values are illustrative; the request set include.markdown to true.
{
"success": true,
"result": {
"text": "Both phones are strong choices. The iPhone 17 favours...",
"markdown": "Both phones are strong choices.\n\n## Camera\nThe iPhone 17 favours...",
// Sources in order. position is an integer here.
"sources": [
{
"position": 1,
"label": "iPhone 17 vs Galaxy S26: camera test",
"url": "https://www.example-tech.de/vergleich/iphone-17-galaxy-s26",
"description": "Side-by-side photos in low light..."
},
{
"position": 2,
"label": "Galaxy S26 review",
"url": "https://www.example-mobile.com/reviews/galaxy-s26",
"description": "Battery life and display..."
}
],
// One entry per (chip, source). citationPillId is 1-based and shared by every
// source in the same chip; position points into result.sources.
// label is always present but may be "" -- fall back to domain.
"citationPills": [
{ "label": "iPhone 17 vs Galaxy S26: camera test", "citationPillId": 1, "url": "https://www.example-tech.de/vergleich/iphone-17-galaxy-s26", "domain": "www.example-tech.de", "position": 1 },
{ "label": "", "citationPillId": 1, "url": "https://www.example-mobile.com/reviews/galaxy-s26", "domain": "www.example-mobile.com", "position": 2 }
]
// "places" appears on lodging and local answers, e.g.:
// "places": [{ "index": 1, "title": "Example Hotel Alfama", "link": "https://www.google.com/...",
// "rating": 4.5, "reviews": 1320, "type": "Hotel", "price": "€142" }]
}
}
Parsing notes:
places[]requiresindex,titleandlink;thumbnail,rating,reviews,type,descriptionandpriceare optional.citationPillsis omitted when the answer has no chips.include.htmlis not available on Gemini; a request that sets it is refused with a 400.
Synchronous calls
Synchronous calls suit dashboards and ad-hoc checks. Each costs 12.3 credits, waits up to five minutes, and holds one concurrency slot.
Python (requests)
import os
from collections import Counter
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
resp = requests.post(
f"{API}/v1/monitor/gemini",
headers=HEADERS,
json={"prompt": "iPhone 17 or Galaxy S26?", "country": "DE", "include": {"markdown": True}},
timeout=330,
)
if resp.status_code != 200:
raise RuntimeError(f"{resp.status_code} {resp.headers.get('X-Request-Id')} {resp.text}")
result = resp.json()["result"]
pills = result.get("citationPills", [])
chips = Counter(p["citationPillId"] for p in pills)
print(result["markdown"][:300])
print("sources:", len(result.get("sources", [])), "chips:", len(chips))
print("domains:", sorted({p["domain"] for p in pills}))
TypeScript (fetch)
const API = "https://api.answerline.dev";
const res = await fetch(`${API}/v1/monitor/gemini`, {
method: "POST",
headers: { Authorization: `Bearer ${process.env.API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ prompt: "Family-friendly hotels in Lisbon", country: "US" }),
signal: AbortSignal.timeout(330_000),
});
if (!res.ok) throw new Error(`${res.status} ${res.headers.get("X-Request-Id")} ${await res.text()}`);
const { result } = await res.json();
for (const place of result.places ?? []) {
console.log(place.index, place.title, place.rating ?? "-", place.reviews ?? "-", place.price ?? "");
}
console.log("credits charged:", res.headers.get("X-Credits-Charged"));
Batches: async tasks and a webhook
Tasks cost 5.6 credits, wait in your queue for a free slot, and can post the finished result to your webhook. POST /v1/async/task/batch takes 1 to 500 tasks; items are admitted independently and answered by index. The batch below gives the hotel prompts a higher priority so they run first.
Python: submit a batch
import datetime
import os
import requests
API = "https://api.answerline.dev"
HEADERS = {"Authorization": f"Bearer {os.environ['API_KEY']}"}
day = datetime.date.today().isoformat()
prompts = {
"phones": "iPhone 17 or Galaxy S26?",
"hotels": "Family-friendly hotels in Lisbon",
}
tasks = [
{
"taskType": "GEMINI",
"payload": {"prompt": prompt, "country": country},
"idempotencyKey": f"gemini-{slug}-{country}-{day}",
"priority": 5 if slug == "hotels" else 1,
"webhook": {"url": "https://your-app.example/hooks/answers"},
}
for slug, prompt in prompts.items()
for country in ("DE", "GB", "US")
]
resp = requests.post(f"{API}/v1/async/task/batch", headers=HEADERS, json=tasks, timeout=60)
resp.raise_for_status()
for item in resp.json()["results"]:
key = tasks[item["index"]]["idempotencyKey"]
print(key, item["task"]["id"] if item["success"] else item["error"]["code"])
Python: receive and verify deliveries (Flask)
Each delivery is the GET /v1/async/task/{taskId} body: task, credits, and response with result or error. Verify the signature over the raw bytes first.
import hashlib
import hmac
import json
import os
import time
from flask import Flask, abort, request
app = Flask(__name__)
def verify(raw: bytes, header: str | None, secret: str, tolerance: int = 300) -> bool:
pairs = [part.split("=", 1) for part in (header or "").split(",") if "=" in part]
stamps = [v for k, v in pairs if k == "t"]
sigs = [v for k, v in pairs if k == "v1"]
if len(stamps) != 1 or not stamps[0].isdigit() or not sigs:
return False
if abs(time.time() - int(stamps[0])) > tolerance:
return False
expected = hmac.new(secret.encode(), stamps[0].encode() + b"." + raw, hashlib.sha256).hexdigest()
return any(hmac.compare_digest(expected, s) for s in sigs)
@app.post("/hooks/answers")
def answers():
raw = request.get_data()
if not verify(raw, request.headers.get("Webhook-Signature"), os.environ["WEBHOOK_SECRET"]):
abort(400)
delivery = json.loads(raw)
if delivery.get("test"):
return "", 204
task = delivery["task"]
if task["status"] == "COMPLETED":
result = delivery["response"]["result"]
places = [p["title"] for p in result.get("places", [])]
print(task["idempotencyKey"], len(result.get("sources", [])), places)
else:
print(task["id"], delivery["response"]["error"]["code"])
return "", 204
Deliveries can repeat, so deduplicate on task.id in your database. See webhooks.
Geo targeting
| Field | Values | Notes |
|---|---|---|
country |
ISO 3166-1 alpha-2 | Required. Gemini answers from 213 countries. GET /v1/countries?model=gemini lists supported codes. |
state |
Two-letter US state code, e.g. CA; DC included |
Only with country: "US". GET /v1/states?country=US lists the codes. |
This endpoint has no hl or city option, so the prompt text is your only language control. For multilingual studies write each prompt in the market’s language and keep the translations fixed across runs; multilingual AI answers covers the design.
Include flags and what they cost
| Flag | Adds to result |
Credits |
|---|---|---|
include.markdown |
markdown |
0 |
include.html |
Not available on Gemini: refused with a 400 | — |
include.rawResponse |
rawResponse, raw provider stream chunks |
0 |
Every Gemini request costs 5.6 credits as a task and 12.3 synchronously, whatever flags you set.
Common workloads
- AI visibility tracking on Google’s assistant alongside ChatGPT. See AI visibility tracking and monitoring Gemini.
- Local and lodging visibility: whether your property or business appears in
places, with what rating and price. See local rank tracking. - Competitor analysis: domains cited for category prompts in each market. See competitor analysis.
- Cross-surface Google comparisons with AI Mode and the AI Overview: the same question on each surface can draw on different pages, so compare
domainsets per surface.
Pitfalls
Place cards only appear for some prompts. A prompt set meant to measure local visibility needs local-intent wording (“family-friendly hotels in Lisbon”); brand questions rarely trigger them.
citationPills[].label can be "". Identify sources by domain or url.
Gemini answers vary between runs, so sample each prompt several times per period and report rates with sample sizes; see sampling vs census. Synchronous calls need a free concurrency slot and otherwise fail with 429 CONCURRENT_LIMIT_EXCEEDED, while 429 RATE_LIMIT_EXCEEDED clears within a second. See rate limits.
How failures are billed
Credits are reserved when a request is accepted and charged only when it completes: 5.6 for a task, 12.3 synchronously. A failed request is charged nothing and the reservation is released, including a task that never starts within 72 hours (QUEUE_WAIT_EXCEEDED). A synchronous call whose connection drops may still complete and be charged, so do not resend it unless the request never reached the server. See credits.
Questions
How many credits does a Gemini request cost?
An async task costs 5.6 credits and a synchronous call 12.3. The include flags for Gemini (markdown and rawResponse) add no credits; include.html is not available on Gemini and is refused with a 400.
How is this different from grounding with Google Search in the Gemini API?
The Gemini API lets you ground your own prompts and model settings on Google Search. This endpoint returns the answer the Gemini app shows its users, with its sources, inline citation pills and place cards as fields.
Does the Gemini endpoint return place or hotel cards?
Yes, when Gemini shows them. result.places carries each card's title, link, rating, review count, type, description and, for lodging, the nightly price as Gemini displays it.
Can I get the full HTML of a Gemini answer?
No. include.html is not available on Gemini: a request that sets it is refused with a 400 and not billed. Store text or markdown instead.
Can I target a US state with Gemini?
Yes. Send state as a two-letter code, for example CA (DC included), together with country US. Gemini answers from 213 countries.