querying.ai scrapes the consumer Gemini app in a real browser session and returns the answer people see, with its cited sources, as JSON. This guide does not call Google's Gemini API. To collect a Gemini answer and its cited sources, submit an authenticated async task, then retrieve the result using the returned task ID. When the task reaches COMPLETED, read response.text and response.sources from the result.
An async task is a request that returns a job identifier before the answer is ready.
What do I need before making the request?
Use Python 3.10 or later and an API key stored in the server environment as QUERYING_API_KEY. The example below uses Python's standard library, so it does not need a third-party package.
export QUERYING_API_KEY='<your-key>'
python3 querying_gemini.py 'best wireless earbuds for commuting'The request uses the GEMINI task type and the US country setting from the public quickstart. The prompt is sent as written.
Source: AI Search Data API Quickstart, https://querying.ai/en/guides/quickstart
How does the Python AI search citations example work?
Save this code as querying_gemini.py. It submits one task, waits for a terminal status, and prints the answer and cited source objects as JSON.
#!/usr/bin/env python3
"""Scrape one consumer Gemini answer and print its text and citations."""
import argparse
import json
import os
import sys
import time
from urllib.error import HTTPError, URLError
from urllib.parse import quote
from urllib.request import Request, urlopen
BASE_URL = "https://api.querying.ai"
def request_json(method, path, api_key, payload=None, timeout=30):
data = None if payload is None else json.dumps(payload).encode("utf-8")
request = Request(
BASE_URL + path,
data=data,
headers={
"Authorization": "Bearer " + api_key,
"Content-Type": "application/json",
},
method=method,
)
try:
with urlopen(request, timeout=timeout) as response:
result = json.load(response)
except HTTPError as error:
raise RuntimeError(f"API returned HTTP {error.code}") from error
except (URLError, TimeoutError) as error:
raise RuntimeError("Network request failed; check task status before resubmitting") from error
if not isinstance(result, dict) or result.get("success") is False:
raise RuntimeError("API did not return a successful response envelope")
return result
def collect_answer(api_key, prompt, country="US", timeout=180, poll_interval=2):
deadline = time.monotonic() + timeout
accepted = request_json(
"POST", "/v1/async/task", api_key,
{"taskType": "GEMINI", "payload": {"prompt": prompt, "country": country}},
timeout=min(30, timeout),
)
task_id = accepted.get("task", {}).get("id")
if not isinstance(task_id, str) or not task_id:
raise RuntimeError("Accepted response is missing task.id")
path = "/v1/async/task/" + quote(task_id, safe="")
while True:
remaining = deadline - time.monotonic()
if remaining <= 0:
raise RuntimeError(f"Polling timed out; resume GET for task {task_id}")
result = request_json("GET", path, api_key, timeout=min(30, remaining))
status = result.get("task", {}).get("status")
if status == "COMPLETED":
response = result.get("response")
if not isinstance(response, dict):
raise RuntimeError("Completed task is missing response")
if "text" not in response or "sources" not in response:
raise RuntimeError("Completed Gemini response is missing text or sources")
return {"task_id": task_id, "text": response["text"], "sources": response["sources"]}
if status == "FAILED":
raise RuntimeError(f"Task {task_id} failed; inspect its result in the dashboard")
if status not in {"QUEUED", "RUNNING"}:
raise RuntimeError(f"Task {task_id} returned an unexpected status: {status}")
time.sleep(min(poll_interval, max(0, deadline - time.monotonic())))
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("prompt", nargs="?", default="best wireless earbuds for commuting")
parser.add_argument("--country", default="US")
parser.add_argument("--timeout", type=int, default=180)
parser.add_argument("--poll-interval", type=int, default=2)
args = parser.parse_args()
if args.timeout <= 0 or args.poll_interval <= 0:
parser.error("--timeout and --poll-interval must be positive")
api_key = os.environ.get("QUERYING_API_KEY")
if not api_key:
parser.error("Set the QUERYING_API_KEY environment variable first")
try:
answer = collect_answer(api_key, args.prompt, args.country, args.timeout, args.poll_interval)
except (RuntimeError, ValueError) as error:
print(str(error), file=sys.stderr)
return 1
print(json.dumps(answer, ensure_ascii=False, indent=2))
return 0
if __name__ == "__main__":
sys.exit(main())The default polling interval is two seconds and the example's local waiting limit is 180 seconds. These are example settings, not a service response-time promise. If that limit expires, use the task ID in the error message to continue retrieving the same task.
Source: AI Search Data API Quickstart, https://docs.querying.ai/quickstart
Which response fields should I use?
The submission returns task.id. Retrieval reports task.status; the completed result includes response.text and response.sources. Source objects in the quickstart show position, url and label.
Store your prompt, country and collection time next to the result when you compare repeated runs. Keep the source URLs so someone reviewing the answer can inspect its citations.
Source: AI Search Data API Quickstart, https://docs.querying.ai/quickstart
What did one live request return?
A single GEMINI request using the prompt and US setting above completed on 4 October 2026. Its response.text contained 3,249 characters and response.sources contained six source objects; the response also included shoppingCards and inlineProducts.
The example reads text and source objects and leaves the extra fields unchanged. This is one observed result, rather than an expected answer size for other prompts.
Frequently asked questions
Is this the same as the Gemini API?
No. The Gemini API returns a response from a model endpoint you call with your own Google key. querying.ai scrapes the answer the consumer Gemini app shows to users, with its sources, so you analyze what your customers actually see.
Is an accepted task a completed answer?
An accepted task is queued work, not a completed answer. Retrieve it until task.status is COMPLETED, or inspect the error if it is FAILED.
How long can I retrieve a completed task?
Public polling is available for 24 hours after completion. Save the result inside that window; the quickstart describes expired completed tasks returning 404.
Can I receive the result without polling?
Add webhook.url when submitting the task to receive a terminal result on your HTTPS endpoint. Implement the documented signature verification, retries and deduplication before using a webhook.
Does the Python example need a package installation?
The example uses Python's standard library. Keep the key in the server environment and run the script with python3.
Sources: AI Search Data API Quickstart, https://docs.querying.ai/quickstart ; Webhook steps in the public quickstart, https://querying.ai/en/guides/quickstart
Create an account and API key at https://querying.ai/en to run your first querying.ai request.