Skip to main content

epovest

The official Python SDK for the Epovest API.

With Epovest, businesses make AIs recommend them. From the questions their customers ask to the sources that shape the answers, everything is measured, dated and verifiable. And everything leads to action: where to appear, what to fix, and proof of what changed.

The assistants it measures are ChatGPT, Claude, Gemini, Perplexity, Mistral and Grok.

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.1.tar.gz (47.4 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.1-py3-none-any.whl (40.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: epovest-1.0.1.tar.gz
  • Upload date:
  • Size: 47.4 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.1.tar.gz
Algorithm Hash digest
SHA256 d4441d3cec1bae9d53491216d0bedaa1affc4d87fa2a4ac399c5895a36e8e069
MD5 fe24ff5a36c4c3078266bdd84363b120
BLAKE2b-256 4179f50b57d004be2952cb63c4d763050546761ce86aeb4148cfbd2d6c294db8

See more details on using hashes here.

File details

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

File metadata

  • Download URL: epovest-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 40.8 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.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e8e19f1446792dd9553a0d5bfaf98d34e58dac14a68fc2ef063c2c292a325081
MD5 83ddc8ac3a8c9d6b05b76df4e87fb4d8
BLAKE2b-256 684520ecbe6d6d5c6f67db0e1019738e54e2ec0222ad2e8a48c4e345e0282041

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 files

1.0.0

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