AnswerLine Start free

Engines

Gemini API

Gemini's grounded answer with its sources, inline citations and comparison tables as shopping cards.

Price

$0.60 per 1,000 requests at Starter, down to $0.42 at Max, deducted from your prepaid USD balance.

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, up to 2,000 characters. A longer prompt is refused with a 400 that states its length.
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 A copy of the Gemini page as it was when the answer was captured, stored on our file host and linked in `html`, to check the answer against. It costs extra, priced at what the page costs us to load, at the task's own margin.
include.rawResponse boolean Return the answer as normalized stream events. For the engine's answer as received, use `include.rawWire`.
include.rawWire boolean Return the engine's own response as received in result.rawWire: the answer stream, the rendered results pages or the RSS feed, without Gemini's detected location or ChatGPT's session tokens; null where it was not captured. Free. See https://answerline.dev/docs/output-formats#raw-wire
include.screenshot boolean Return a full-page PNG screenshot of the engine's page as it showed the finished answer, in result.screenshot: a link, or one link per results page on Google Search. On ChatGPT, Gemini, AI Mode, Google Search, Perplexity and Grok; other engines refuse it with a 400. Absent when the answer was read without a rendered page or its page was not available. Free. See https://answerline.dev/docs/output-formats#screenshots
state string State code for targeting below the country level (e.g., "CA"; "DC" included). Allowed only when country is "US". Served for Gemini only; ChatGPT, Copilot, Perplexity and Grok refuse `state` with a 400, uncharged. "DC" fails uncharged: no exit serves it.
answerDepth "any" | "full" | "quick" How deep an answer the engine writes, where it offers a choice. any (the default): a full answer when the engine can give one now, else its short one. full: a full answer or a retryable error, never charged. quick: the short one, the fastest. Perplexity offers the choice; every other engine has one depth and ignores the field. provenance.answerDepth says what was served. Same price for every value. See https://answerline.dev/docs/engine-features/#answer-depth

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.
shoppingCardsobject[]Product cards Gemini shows beside a shopping-related answer, in display order. Absent when the answer has none.

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

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 product cards beside shopping answers, with prices, stores and ratings, which come back here as shoppingCards. For e-commerce brands that card is the recommendation. Place cards (places) are not returned today. 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": "example-tech.de", "position": 1 },
      { "label": "", "citationPillId": 1, "url": "https://www.example-mobile.com/reviews/galaxy-s26", "domain": "example-mobile.com", "position": 2 }
    ]
    // "shoppingCards" appears when Gemini shows products, e.g.:
    // "shoppingCards": [{ "position": 1, "title": "Example Phone 17", "productId": "1234567890",
    //   "productLink": "https://www.google.com/search?q=...&prds=productid%3A1234567890",
    //   "price": { "raw": "€949.00", "value": 949, "currency": "EUR" }, "store": "Example Shop",
    //   "storeCount": 4, "rating": 4.6, "reviews": 812, "brand": "Example" }]
  }
}

Parsing notes:

Synchronous calls

Synchronous calls suit dashboards and ad-hoc checks. Each costs $0.0006, the same as a task, and waits up to five minutes.

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: "Best cordless stick vacuum under $400", 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 card of result.shoppingCards ?? []) {
  console.log(card.position, card.title, card.price?.raw ?? "-", card.store ?? "-", card.rating ?? "-");
}
console.log("charged (USD):", res.headers.get("X-Credits-Charged"));

Batches: async tasks and a webhook

Tasks cost $0.0006, wait in your queue, 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 shopping 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?",
    "vacuums": "Best cordless stick vacuum under $400",
}
tasks = [
    {
        "taskType": "GEMINI",
        "payload": {"prompt": prompt, "country": country},
        "idempotencyKey": f"gemini-{slug}-{country}-{day}",
        "priority": 5 if slug == "vacuums" 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"]
        products = [c["title"] for c in result.get("shoppingCards", [])]
        print(task["idempotencyKey"], len(result.get("sources", [])), products)
    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 html: a link to a copy of the Gemini page (HTML snapshots) 0
include.rawResponse rawResponse, the answer as normalized stream events 0
include.rawWire rawWire: the answer as Gemini sent it, the detected location removed, when it was captured; off by default (what it holds) 0
include.screenshot screenshot: a link to a full-page PNG of the Gemini page once the answer finished, when the page was shown (details) 0

Every Gemini request costs $0.0006, whatever flags you set.

Every answer is read from gemini.google.com loaded in a desktop browser, so result.provenance reads interface desktop-web and searchMode engine-default (all fields).

Common workloads

Pitfalls

Shopping cards appear only on answers where Gemini shows products. Read shoppingCards with a default, and report the share of answers that carried 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. There is no concurrency cap and no request rate limit. See rate limits.

How failures are billed

The cost is reserved when a request is accepted and charged only when it completes: $0.0006. A failed request is charged nothing and the reservation is released, including a task that never starts within 4 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 much does a Gemini request cost?

A request costs $0.0006. The include flags for Gemini (markdown, rawResponse and rawWire) add nothing, and neither does include.html.

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 shopping cards as fields.

Does the Gemini endpoint return place or shopping cards?

Shopping cards, yes: result.shoppingCards carries each product Gemini shows beside the answer, with its price, store, rating and Google Shopping link. Place cards (result.places) are not returned today.

Can I get the full HTML of a Gemini answer?

Yes. Set include.html and result.html links a copy of the Gemini page as it was when the answer was captured, stored on files.answerline.dev. The copy opens with scripts off.

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