AnswerLine Start free

Engines

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

Or queue it with POST /v1/async/task and taskType: "GEMINI".

Countries

213 countries, plus US states.

Coverage

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

* required. Nested fields are written with a dot, e.g. include.markdown.

FieldTypeDescription
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 resultTypeDescription
textstringText of Gemini's reply
markdownstringGemini's reply as Markdown
sourcesobject[]Sources that Gemini cited
citationPillsobject[]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.
placesobject[]Place cards included in lodging and local answers

Full field definitions are in the API reference.

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:

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

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.

Other engines