Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tryprofound.com/llms.txt

Use this file to discover all available pages before exploring further.

The headline number on every Visibility tile in the Profound app, plus the +1.0 pp badge beside it. Same call twice — current window and prior window of equal length — and diff the two scores.

How this example works

  1. Aggregate, not time-series. Omit date from dimensions; filter on asset_name. Response has one row with a single traffic-weighted score.
  2. Run the same call twice — once for the current window, once for the prior window of equal length — then subtract.
  3. Read positions from info.query.metrics. Don’t hardcode array indices.
import os
from datetime import date, timedelta
from profound import Profound

client = Profound(api_key=os.environ["PROFOUND_API_KEY"])

# What to fetch — replace with your own values.
CATEGORY_NAME = "<your-category-name>"
ASSET_NAME    = "<your-asset-name>"
METRIC        = "visibility_score"   # or share_of_voice, average_position, ...
DAYS          = 7
INCLUSIVE_END = date(2026, 5, 11)    # the last day of your current window


def get_visibility_score(category_id, asset_name, metric, start, end):
    """Aggregate score for one asset in one window."""
    res = client.reports.visibility(
        category_id=category_id,
        start_date=start.isoformat(),
        end_date=end.isoformat(),
        metrics=[metric],
        filters=[{"field": "asset_name", "operator": "is", "value": asset_name}],
    )
    order = res.info.query["metrics"]
    return res.data[0].metrics[order.index(metric)]


def current_and_prior_windows(inclusive_end, days):
    """Two (start, end_exclusive) pairs of equal length, back-to-back."""
    current = (
        inclusive_end - timedelta(days=days - 1),
        inclusive_end + timedelta(days=1),     # +1 day → exclusive end
    )
    prior = (current[0] - timedelta(days=days), current[0])
    return current, prior


# Helpers — translate human-readable names into the IDs the report API needs.

def find_category_id(name):
    """Return the UUID of the category whose name matches (case-insensitive)."""
    for c in client.organizations.categories.list():
        if c.name.lower() == name.lower():
            return c.id
    raise ValueError(f"No category named {name!r}")


def find_asset_name(category_id, name):
    """Return the canonical asset name (case-insensitive) inside the category."""
    for a in client.organizations.categories.assets(category_id):
        if a.name.lower() == name.lower():
            return a.name
    raise ValueError(f"No asset named {name!r} in this category")


# Resolve names → IDs, then run both windows.
category_id    = find_category_id(CATEGORY_NAME)
asset_name     = find_asset_name(category_id, ASSET_NAME)
current, prior = current_and_prior_windows(INCLUSIVE_END, DAYS)

current_score = get_visibility_score(category_id, asset_name, METRIC, *current)
prior_score   = get_visibility_score(category_id, asset_name, METRIC, *prior)
delta_pp      = (current_score - prior_score) * 100

print(f"{asset_name} {METRIC}: {current_score:.1%}  ({delta_pp:+.1f} pp vs prev period)")

Available metrics

Swap visibility_score for any of these to get a different KPI tile:
MetricWhat it isFormatDirection
visibility_scoreFraction of answers the asset appeared inPercentHigher is better
share_of_voiceFraction of all mentions the asset accounts forPercentHigher is better
average_positionAverage rank when the asset is mentionedRankLower is better
mentions_countRaw count of mentionsNumberHigher is better
executionsNumber of prompts runNumberHigher is better
For average_position, flip the delta’s color logic — a negative delta is an improvement.

Common follow-ups

Plot the same metric over time

Add date to dimensions to build a daily line chart.

Headline + daily series, together

Why the headline call and the chart call return different numbers.