Skip to main content

epovest

The official Python SDK for the Epovest API.

Epovest is an AI visibility platform: it measures how AI assistants such as ChatGPT, Claude, Gemini, Perplexity, Mistral and Grok answer the questions your market asks (which names they cite, with which sources, how that changes over time), and gives you the levers to shape those answers.

The whole REST API v1 is here, one method per route, generated from the OpenAPI 3.1 description the API serves at epovest.com/docs/openapi.json.

  • Synchronous and asynchronous clients, on httpx.
  • Typed: every named object of the API is a TypedDict, and every method says what it answers.
  • Query parameters are spelled out as keyword arguments, so your editor lists them.

Install

pip install epovest

Python 3.11 and above.

Quickstart

import os

from epovest import Epovest

epovest = Epovest(api_key=os.environ["EPOVEST_API_KEY"])

# What is already measured
trackers = epovest.list_trackers()["trackers"]

# Set up a new measurement, and start it
tracker = epovest.create_tracker(
    body={
        "title": "Market watch",
        "prompts": ["Which GEO tracking solution should I choose?"],
        "engines": ["chatgpt", "claude"],
        "frequency": "weekly",
        "resolution": "hd",
        "keywords": [{"keyword": "Epovest", "favorite": True}],
        "analysts": ["keyword_presence", "share_of_voice"],
    }
)["tracker"]

epovest.start_tracker(tracker["id"])

# Read the series
scores = epovest.get_results(tracker["id"], analyst="keyword_presence")["scores"]

Asynchronous, same names:

from epovest import AsyncEpovest

async with AsyncEpovest(api_key=os.environ["EPOVEST_API_KEY"]) as epovest:
    answers = await epovest.get_responses(tracker_id, engine="claude", tone="negative")

Authentication

Every call carries an API key of your account. The owner creates one in the app, at /account/api-keys; the secret starts with epo_ and is shown once, at creation.

Scopes are chosen at creation: read, which every key has, and write, for creating and changing things. Each method names the scope it takes in its docstring.

epovest = Epovest(
    api_key=os.environ["EPOVEST_API_KEY"],
    timeout=30.0,
    max_retries=2,
    headers={"User-Agent": "acme-reporting/2.1"},
)

The client holds a connection pool: use it as a context manager, or call close() (aclose() on the asynchronous one) when you are done.

What you can call

70 methods, named after the operation they wrap, and the name is the same one the MCP tool carries:

Area Methods
Trackers list_trackers, get_tracker, create_tracker, update_tracker, start_tracker, survey_now, pause_tracker, archive_tracker
Results list_surveys, get_results, get_responses
Keyword discovery list_keyword_discoveries, accept_keyword_discovery, dismiss_keyword_discovery, restore_keyword_discovery
Projects and canon list_projects, create_project, rename_project, get_canon, update_project_canon, archive_project, get_link_targets, set_link_targets
Surfaces list_surfaces, create_surface, update_surface, tick_surface_checklist, add_surface_check, update_surface_check, delete_surface_check, restore_surface_check, delete_surface, restore_surface, convert_surface_to_corroboration
Corroborations list_corroborations, create_corroboration, update_corroboration, verify_corroboration, set_registry_monitoring, archive_corroboration, convert_corroboration_to_surface, list_corroboration_candidates, dismiss_corroboration_candidate
Logbook get_logbook, create_logbook_entry, update_logbook_entry, delete_logbook_entry, restore_logbook_entry
Quests list_quests, create_quest, update_quest, complete_quest, dismiss_quest, reopen_quest
Competitor scans list_competitor_scans, get_competitor_scan, create_competitor_scan, start_competitor_scan, update_competitor_scan
Atlas of sources list_sources, get_source, list_source_channels
Account, credits, usage get_account_settings, update_account_settings, get_credits, topup_credits, get_usage
Support contact_support, list_support_threads, get_support_thread

Path parameters come first, the query parameters follow as keyword arguments, and a request body goes in body:

epovest.update_surface(surface_id, body={"languages": ["en", "pt-br"]})
epovest.list_sources(domain="wikipedia.org", sort="aa_claude", per_page=100)

Types

Every named object of the API is exported as a TypedDict, and every method is annotated with what it answers:

from epovest import Credits, Tracker


def shortfall(credits: Credits) -> int:
    return max(0, credits["min_topup_minor"] - credits["available_minor"])

Pagination

GET /sources, GET /trackers/{id}/responses and GET /usage are paginated, and page is clamped: asking for page 99 of 3 answers page 3. paginate reads that the way the API intends, and stops on the last page:

for page in epovest.paginate(lambda page: epovest.list_sources(page=page, per_page=100)):
    for source in page["sources"]:
        print(source["domain"], source["engines"])

Errors

A refused call raises an EpovestError carrying the slug of the refusal. Branch on code, which is stable, rather than on the message, which is written for a person. Everything the envelope carries next to the two of the envelope stays readable in body:

from epovest import EpovestError

try:
    epovest.start_tracker(tracker_id)
except EpovestError as error:
    if error.code == "insufficient_credits":
        # The answer carries what a survey costs, what is available, and where to top up.
        print(error.body["cost_per_survey_minor"], error.body["available_minor"], error.body["top_up_url"])
    raise

A request that never reached Epovest raises an EpovestConnectionError, whose cause carries what the platform reported.

Retries. A 429 rate_limited is sent again whatever the verb, after the wait the Retry-After header names: the budget refuses the call before it runs. A network failure or a 5xx is sent again on reads alone, so a write that may have landed is yours to replay, when you decide it should be. max_retries sets how many times, and 0 turns it off.

Also available as MCP

The same surface is served as MCP tools at https://mcp.epovest.com/mcp, for agents that speak the Model Context Protocol: one tool per route, the same keys and the same error envelopes, and the tool names are the method names of this SDK. See epovest.com/docs/api.md.

Reference

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

epovest-1.0.0.tar.gz (47.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

epovest-1.0.0-py3-none-any.whl (40.7 kB view details)

Uploaded Python 3

File details

Details for the file epovest-1.0.0.tar.gz.

File metadata

  • Download URL: epovest-1.0.0.tar.gz
  • Upload date:
  • Size: 47.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for epovest-1.0.0.tar.gz
Algorithm Hash digest
SHA256 63b253b5aad6bc1b5a3067a5677e667fdcf06f0ae8442d364499e5a03fe4a87e
MD5 4f7b3994bb548fdc3b1c149bcffb6626
BLAKE2b-256 d1868b9884d88785576f384167ec15453538a13e748586765c0da91135b47ff3

See more details on using hashes here.

File details

Details for the file epovest-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: epovest-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 40.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for epovest-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 946f446e0a7668b2ea65e65d210e4510dfef585923b7778d78fcd4939f8313a4
MD5 e65fa41668406d30435ac946ee58d931
BLAKE2b-256 7f7f6bcca401a87a6291338bd1baff2aa57e445cc8c82179e35156bd0e40b8cc

See more details on using hashes here.

Release history Release notifications | RSS feed

1.0.1

2 files

This release

1.0.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page