Metadata-Version: 2.4
Name: answerline
Version: 0.3.0
Summary: Python client for the AnswerLine answer and search monitoring API.
Author-email: AnswerLine <support@answerline.dev>
License-Expression: MIT
Project-URL: Homepage, https://answerline.dev
Keywords: answerline,ai,llm,search-api,monitoring,chatgpt,perplexity,gemini
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1,>=0.27
Dynamic: license-file

# answerline

Python client for the [AnswerLine](https://answerline.dev) API — structured JSON answers from AI assistants
(ChatGPT, Perplexity, Gemini, Copilot, Grok) and Google surfaces (Search, News, AI Overview, AI Mode), on httpx.

ChatGPT, Gemini, Copilot, Google Search (with AI Overview), AI Mode and Google News are served today; Perplexity and Grok are paused for now and coming back soon. [Coverage](https://answerline.dev/docs/coverage) lists what is served and from which countries. A monitor call to a paused engine fails with a 404 and an async task with a 422, each saying so, and nothing is billed.

```bash
pip install answerline
```

```python
from answerline import Client

with Client("sk_...") as client:
    # Synchronous monitor calls: one method per engine (chatgpt, perplexity, gemini,
    # copilot, grok, aimode, google, google_news), or client.monitor(engine, **body).
    answer = client.monitor.chatgpt(prompt="iPhone 17 or Galaxy S26?", country="US")

    # Async tasks: create, poll, or run to a terminal status.
    task = client.async_tasks.run(
        "CHATGPT",
        {"prompt": "iPhone 17 or Galaxy S26?", "country": "US"},
        idempotency_key="phone-compare-1",
    )

    client.credits()  # {"remaining", "perCycle", "cycleResetsAt"}
```

`AsyncClient` has the same methods, awaitable. `async_tasks.create_batch` submits up to 500 tasks in one call;
`async_tasks.wait` polls a task to COMPLETED or FAILED; `async_tasks.status` and `async_tasks.clear_queue` manage
the queue. Prefer a webhook (`webhook_url=` on the task) over polling for many tasks — verify deliveries with
`verify_webhook(body, header, secret)` against the `Webhook-Signature` header.

Calls raise `ApiError` (`status`, stable `code`, parsed `body`, and `meta` with the rate-limit, concurrency, credit
and request-id headers); transport failures raise `TimeoutError` or `ConnectionError`. 429s and safely replayable
failures are retried with jittered backoff; `timeout`, `sync_timeout`, `max_retries` and `http_client` are
constructor options.

## Measuring brand visibility

One answer is one draw from an engine that varies its answers. `visibility.measure` asks each engine the same prompt
`samples` times as separate async tasks and returns the results per engine; with a `brand` it also runs
`brand_check` over them: how many answers mention the brand (with a Wilson 95% interval), its average rank among the
brands you list, how many cite its own domains, and which domains get cited when it is absent.

```python
from answerline import brand_check

measured = client.visibility.measure(
    "Which CRM is best for a 10-person sales team?",
    ["chatgpt", "gemini"],
    "US",
    samples=12,
    rounds=4,
    spacing_seconds=2 * 60 * 60,  # 4 rounds of 3, two hours apart
    brand={"name": "Acme CRM", "aliases": ["Acme"], "domains": ["acme.com"]},
    competitors=[{"name": "Globex"}, {"name": "Initech"}],
)
brand = measured["chatgpt"]["brand_check"]["brand"]  # mentioned, runs, interval, average_rank, ...

# Or check results you already have, one engine at a time:
brand_check(results, {"name": "Acme CRM"}, [{"name": "Globex"}])
```

Samples spread over hours measure how much the engine varies its own answer to that prompt from that country, not
what different users see. Small samples give wide intervals: take 10 or more per engine. See
[Measuring visibility](https://answerline.dev/docs/measuring-visibility).
