Technical SEO

AI Search API Access: Integrate ChatGPT, Perplexity & Gemini Search

Updated 8 min read Daniel Shashko
AI Search API Access: Integrate ChatGPT, Perplexity & Gemini Search
AI Summary
Three AI platforms expose production APIs for citation share-of-voice monitoring in 2026. Perplexity Sonar API (POST https://api.perplexity.ai/chat/completions, OpenAI-compatible) returns a top-level citations array of URLs; Sonar model costs $1/$1 per million tokens plus $5 per 1,000 requests. OpenAI Responses API with web_search_preview tool embeds cited URLs as url_citation annotations inside message content; web search costs $10 per 1,000 calls. Google Gemini API with google_search grounding tool returns groundingMetadata.groundingChunks with URI and title per cited source, billed per search query executed. Our May 2026 study of 153,425 citations found Gemini adds text fragment anchors to 84.1% of its 13,487 citation URLs. Implementation pattern: build a 50-200 query prompt panel across brand, competitor, category, and buyer-intent queries; run 3-5 samples per query per day; parse and normalize citation URLs; log share of voice per domain per run. The open-source GEO/AEO Tracker (github.com/danishashko/geo-aeo-tracker, over 150 stars, MIT) handles all of this out of the box across six AI models.

Three AI platforms expose production APIs you can use right now to monitor citation share of voice: Perplexity’s Sonar API, OpenAI’s web search tool via the Responses API, and Google’s Gemini API with the google_search grounding tool. Each returns structured citation data in a parseable format. This guide shows you exactly how to call them, what the response shapes look like, and how to wire them into a share-of-voice tracking loop.

AI search has shifted from an emerging channel to a primary discovery surface. Our May 2026 study of 153,425 citations across six platforms established that 76.95% of cited URLs are not in the organic top 10. You cannot infer AI citation share from traditional rank tracking. You need to query the engines directly and parse what they return.

This is the engineering foundation for AI brand visibility tracking. The pattern is the same across all three providers: authenticate with a bearer token, send a prompt, parse citations from the structured response, and log them to a datastore. The differences are in the response shape and pricing model.

The three APIs and what they actually expose

Perplexity Sonar API

The Perplexity Sonar API is the most direct path for citation monitoring. It uses an OpenAI-compatible chat completions format: POST https://api.perplexity.ai/chat/completions with bearer token auth. The response includes a top-level citations array alongside the standard choices object. No parsing of response prose required - the cited URLs are returned as a clean list. Newer responses also carry a richer search_results array with title, URL, and date per source; prefer it when present and fall back to citations.

Available models as of June 2026: sonar and sonar-pro. Sonar is priced at $1 per million input tokens, $1 per million output tokens, plus $5 per 1,000 requests (low search context). Sonar Pro runs $3 per million input, $15 per million output, plus $6 per 1,000 requests. For a monitoring workload of 100 queries per day, Sonar is the cost-efficient choice. Full current pricing is at docs.perplexity.ai/docs/getting-started/pricing.

OpenAI Responses API with web search tool

OpenAI’s web search capability is exposed through the Responses API as a tool. You pass "tools": [{"type": "web_search_preview"}] in your request, and the model decides when to invoke it. The response includes output blocks with type: "web_search_call" and type: "message". Cited sources appear as annotations in the message content with type: "url_citation", each containing url, title, and the text span that was cited.

Pricing for web search: $10 per 1,000 calls for all standard models, with search content tokens billed at the chosen model’s per-token rate. See current pricing at platform.openai.com/api/docs/pricing under the Tools section. For citation monitoring workloads, gpt-5.4-mini at $0.75 per million input tokens plus the $10 per 1,000 search calls is the practical cost floor.

Unlike Perplexity, OpenAI does not return a clean citations array. You extract cited URLs from the annotations field inside message content blocks. The structure is documented but requires a small parsing step covered in the code example below.

Google Gemini API with google_search grounding

Gemini’s grounding tool connects the model to real-time Google Search results. You enable it by passing tools=[types.Tool(google_search=types.GoogleSearch())] in your GenerateContentConfig. The response includes a groundingMetadata field containing groundingChunks (the cited sources, each with uri and title) and groundingSupports (which maps text segments to their source chunks by index).

This is the richest citation structure of the three: you get not just which URLs were cited but which specific text spans are attributed to which source. For citation velocity tracking, this enables sentence-level attribution analysis. Our May 2026 study used Gemini grounding data extensively - Gemini now adds text fragment anchors to 84.1% of its 13,487 citation URLs.

Billing for Gemini 3 models: charged per search query the model executes, not per API call. A single prompt may trigger multiple searches. Detailed pricing is at ai.google.dev/gemini-api/docs/pricing. The Google GenAI SDK (pip install google-genai) is the recommended client.

API comparison

ProviderEndpoint / methodCitation formatAuthCost unit
Perplexity SonarPOST /chat/completions (OpenAI-compat)Top-level citations[] array of URLsBearer tokenPer token + per request
OpenAI web searchResponses API + web_search_preview toolannotations[] inside message contentBearer tokenPer token + $10/1k search calls
Gemini groundinggenerateContent with google_search toolgroundingMetadata.groundingChunks[]API keyPer search query executed

Python implementation pattern

The pattern below queries all three providers with a list of brand-relevant prompts, parses citations from each, and logs share of voice per domain per run. Run it daily via cron. Extend the PROMPT_PANEL list with your actual monitoring queries.

# AI citation share-of-voice tracker
# Queries Perplexity, OpenAI, and Gemini; parses citations; logs SoV per domain.
# Requirements: pip install openai google-genai
import os, json, datetime
from collections import defaultdict
from openai import OpenAI
from google import genai
from google.genai import types

PROMPT_PANEL = [
    "What is the best tool for tracking AI search visibility?",
    "How do I monitor brand citations in ChatGPT and Perplexity?",
    "Which companies offer GEO optimization services?",
    # add 50-200 brand-relevant queries here
]

BRAND_DOMAIN = "organikpi.com"
LOG_FILE = f"citations_{datetime.date.today()}.json"


def query_perplexity(prompt: str) -> list[str]:
    # Returns list of cited URLs from Perplexity Sonar API.
    client = OpenAI(
        api_key=os.environ["PERPLEXITY_API_KEY"],
        base_url="https://api.perplexity.ai",
    )
    response = client.chat.completions.create(
        model="sonar",
        messages=[{"role": "user", "content": prompt}],
    )
    # citations is a top-level field on the raw response
    raw = response.model_dump()
    return raw.get("citations", [])


def query_openai(prompt: str) -> list[str]:
    # Returns list of cited URLs from OpenAI Responses API with web search.
    client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
    response = client.responses.create(
        model="gpt-5.4-mini",
        tools=[{"type": "web_search_preview"}],
        input=prompt,
    )
    urls = []
    for block in response.output:
        if block.type == "message":
            for part in block.content:
                for ann in getattr(part, "annotations", []):
                    if ann.type == "url_citation":
                        urls.append(ann.url)
    return list(set(urls))


def query_gemini(prompt: str) -> list[str]:
    # Returns list of cited URLs from Gemini with google_search grounding.
    client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
    response = client.models.generate_content(
        model="gemini-2.5-flash",
        contents=prompt,
        config=types.GenerateContentConfig(
            tools=[types.Tool(google_search=types.GoogleSearch())]
        ),
    )
    urls = []
    for candidate in response.candidates:
        meta = getattr(candidate, "grounding_metadata", None)
        if meta:
            for chunk in getattr(meta, "grounding_chunks", []):
                uri = getattr(getattr(chunk, "web", None), "uri", None)
                if uri:
                    urls.append(uri)
    return list(set(urls))


def extract_domain(url: str) -> str:
    from urllib.parse import urlparse
    netloc = urlparse(url).netloc
    return netloc.removeprefix("www.")


def run_panel():
    sov: dict[str, dict[str, int]] = {
        "perplexity": defaultdict(int),
        "openai": defaultdict(int),
        "gemini": defaultdict(int),
    }
    total_queries = len(PROMPT_PANEL)
    results = []

    for prompt in PROMPT_PANEL:
        run = {"prompt": prompt, "citations": {}}

        for provider, fn in [
            ("perplexity", query_perplexity),
            ("openai", query_openai),
            ("gemini", query_gemini),
        ]:
            try:
                urls = fn(prompt)
                run["citations"][provider] = urls
                for url in urls:
                    sov[provider][extract_domain(url)] += 1
            except Exception as e:
                run["citations"][provider] = {"error": str(e)}

        results.append(run)

    summary = {
        "date": str(datetime.date.today()),
        "total_queries": total_queries,
        "brand_domain": BRAND_DOMAIN,
        "share_of_voice": {
            p: {
                "brand_citations": sov[p].get(BRAND_DOMAIN, 0),
                "brand_sov_pct": round(
                    sov[p].get(BRAND_DOMAIN, 0) / total_queries * 100, 1
                ),
                "top_10_domains": sorted(
                    sov[p].items(), key=lambda x: x[1], reverse=True
                )[:10],
            }
            for p in sov
        },
        "runs": results,
    }

    with open(LOG_FILE, "w") as f:
        json.dump(summary, f, indent=2)
    print(f"Logged {total_queries} queries. Brand SoV:")
    for p, data in summary["share_of_voice"].items():
        print(f"  {p}: {data['brand_sov_pct']}% ({data['brand_citations']}/{total_queries})")


if __name__ == "__main__":
    run_panel()

Prompt panel design

The quality of your share-of-voice signal depends entirely on your prompt panel. A panel of 50-200 queries split across four categories gives stable signal without runaway API cost:

  • Brand queries: your brand name, product names, founder name, tagline variations
  • Competitor queries: each key competitor’s brand and product names
  • Category queries: “best [category] tool”, “top [category] services”, “[category] alternatives”
  • Buyer intent queries: “how to [solve problem your product solves]”, “what is [your category]”

Run each query three to five times per day and average the results. AI search responses are non-deterministic: a single run per query will show high variance. Three to five samples per query per provider gives a stable daily share-of-voice number you can trend over time. This is the core of what we measure in our AI search share of voice framework.

The share of voice vs search volume distinction matters here: you are measuring how often your brand appears in AI responses relative to the total response pool, not how many people search for you. These are different metrics with different strategic implications.

Response normalization and common pitfalls

Three issues trip up most teams building this in-house.

URL canonicalization. The same article cited as https://example.com/blog/post/, http://example.com/blog/post, and https://www.example.com/blog/post is one citation, not three. Strip protocol, normalize trailing slash, and strip www. before counting. Without this, your dashboards will undercount your own domain and overstate diversity.

Provider-specific citation structures. Perplexity returns a clean array. OpenAI embeds citations inside message content annotations. Gemini puts them in groundingMetadata inside each candidate. Write a separate extractor function per provider as shown above. Do not try to handle all three in one function.

Region and persona drift. AI search results vary by user region. Always run your monitoring queries without geo-specific headers unless you are explicitly monitoring a regional market. Use consistent model versions; model updates shift citation behavior and break time-series comparisons. Log the model version in every run. We track these behavioral shifts as part of our DIY AI brand visibility audit methodology.

The open-source alternative

If you would rather not build the infrastructure yourself, the open-source GEO/AEO Tracker at github.com/danishashko/geo-aeo-tracker (over 150 stars, MIT license) handles multi-model tracking, citation parsing, share-of-voice scoring, scheduled batch runs, and a competitor benchmarking dashboard out of the box. It tracks six AI models simultaneously and stores data locally first with optional Supabase cloud sync. Your query panel and brand data never leave your infrastructure.

The tracker’s AEO Audit tab also checks for schema markup gaps, llms.txt presence, BLUF density, and heading structure - all the on-page signals that affect whether your content gets cited in the first place. For teams that want the monitoring data without the engineering overhead, it is the starting point we recommend before considering any commercial AI search competitive intelligence tools.

What to do with the data

Citation share of voice is a leading indicator. Changes in AI citation rates show up weeks before changes in branded organic traffic or direct navigation. Use the data to:

  • Identify which competitor pages are displacing you on specific query types, then analyze what they have that you do not (schema, recency, source authority)
  • Track the impact of content updates and schema changes with a four-to-eight week measurement window
  • Spot citation drops on important queries before they become traffic problems
  • Map which AI engines cite you versus competitors, since citation behavior varies significantly across platforms as documented in our AI brand visibility metrics guide

The citation velocity framework extends this into time-series analysis: how fast is your citation share growing or declining on each query cluster. That metric predicts future AI search presence more accurately than point-in-time share of voice.

Start with our DIY AI brand visibility audit to establish a manual baseline before investing in automated tracking. Automated monitoring amplifies what is already working. Content that AI engines are not citing requires a content fix first.