Competitive position across topics
The dashboard’s Industry rankings widget shows your top-mentioned competitors across all of your tracked conversations. Sometimes you want the finer-grained view: for each topic you track, where do you actually rank?
This recipe uses tpc analytics explore to answer that in one script — no dashboard clicking required.
Prerequisites
- The CLI installed and authenticated. See Installation and Authentication.
tpc product switch <product-slug> set to the product you want to check.
jq installed locally.
The script
Each topic requires its own ranking lookup, so this runs them concurrently instead of one at a time — each result is captured to a temp file so parallel output doesn’t interleave, then printed back in the original topic order.
#!/usr/bin/env bash
set -euo pipefail
YOUR_PRODUCT=$(tpc auth whoami | awk -F': ' '/^Product:/{print $2}')
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
check_topic() {
local topic_id="$1" topic="$2"
local you
you=$(tpc analytics explore --metric sov --by competitor --topic "$topic_id" --top 100 --json \
| jq -r --arg name "$YOUR_PRODUCT" '.rows[] | select(.label == $name) | "\(.rank)\t\(.sov)"')
if [ -n "$you" ]; then
local rank sov
rank=$(echo "$you" | cut -f1)
sov=$(echo "$you" | cut -f2)
printf "%-14s rank #%-4s sov %s%%\n" "$topic" "$rank" "$sov"
else
printf "%-14s not in top 100\n" "$topic"
fi
}
topic_ids=()
while IFS=$'\t' read -r topic_id topic; do
topic_ids+=("$topic_id")
check_topic "$topic_id" "$topic" > "$tmpdir/$topic_id" &
done < <(tpc analytics explore --metric sov --by topic --json | jq -r '.rows[] | [.key, .label] | @tsv')
wait
for topic_id in "${topic_ids[@]}"; do
cat "$tmpdir/$topic_id"
done
Run it:
chmod +x competitive-position.sh
./competitive-position.sh
Example output:
comparison rank #1 sov 58.8%
feature rank #2 sov 42.4%
use-case rank #3 sov 36.0%
budget rank #2 sov 34.2%
industry rank #22 sov 18.0%
How it works
tpc auth whoami prints your active product’s name, which is what you look for in each topic’s ranking.
tpc analytics explore --metric sov --by topic lists every topic you track, with each topic’s ID (key) and name (label).
- For each topic, a background
check_topic call runs tpc analytics explore --metric sov --by competitor --topic <topicId> — the same ranking view as the dashboard’s Industry rankings widget, but scoped to one topic instead of everything. All topics are looked up concurrently instead of waiting on each one in turn.
jq picks out the row matching your product’s name and writes its rank and share of voice to that topic’s temp file.
wait blocks until every background lookup finishes, then the results are printed back in the original topic order (not finish order).
The speedup scales with how many topics you track. With a handful of topics, most of the wall-clock time is per-invocation overhead (auth and org resolution), so parallelizing saves less than you’d expect. With dozens of topics, running them concurrently instead of sequentially is the difference between minutes and seconds.
--top 100 is the highest page size the API allows in one page. If a topic has more than 100 tracked competitors and you don’t show up, page through with --page 2 (see tpc analytics explore --help) rather than assuming you’re unranked.
Variations
Just want the overall ranking, not broken out by topic?
tpc analytics explore --metric sov --by competitor --last 30d
Want the raw JSON for a dashboard or Slack digest instead of a formatted table? Drop the printf lines and pipe the jq output directly — every command in this recipe supports --json.
Only care about a couple of topics? Skip the topic-listing step and hardcode the topic IDs you already know:
tpc analytics explore --metric sov --by competitor --topic <topicId>
Show competitors around your rank
Knowing your rank and share of voice in a topic is useful, but it doesn’t say who you’re actually competing against right at that position. This script takes one topic and shows the competitors ranked immediately above and below you — reusing the exact same tpc analytics explore --by competitor --topic response as the main script above, since every competitor’s rank is already in that one call. No extra API request needed.
#!/usr/bin/env bash
set -euo pipefail
TOPIC_ID="$1"
WINDOW="${2:-2}"
YOUR_PRODUCT=$(tpc auth whoami | awk -F': ' '/^Product:/{print $2}')
rows=$(tpc analytics explore --metric sov --by competitor --topic "$TOPIC_ID" --top 100 --json \
| jq -r --arg name "$YOUR_PRODUCT" --argjson window "$WINDOW" '
.rows as $rows
| ($rows | map(.label) | index($name)) as $idx
| if $idx == null then empty else
$rows[(if $idx - $window < 0 then 0 else $idx - $window end):($idx + $window + 1)][]
| [.rank, .label, .sov, (if .label == $name then "you" else "" end)] | @tsv
end
')
if [ -z "$rows" ]; then
echo "$YOUR_PRODUCT is not in the top 100 for this topic"
exit 0
fi
echo "$rows" | while IFS=$'\t' read -r rank label sov marker; do
if [ "$marker" = "you" ]; then
printf "%-4s %-20s %s%% <-- you\n" "$rank" "$label" "$sov"
else
printf "%-4s %-20s %s%%\n" "$rank" "$label" "$sov"
fi
done
Run it with a topic ID (get one from tpc analytics explore --metric sov --by topic --json, or from the topic-listing step of the script above):
chmod +x competitors-around-rank.sh
./competitors-around-rank.sh a8bbcb6f-1ee3-462b-98f1-abe8e05e5437
Example output:
19 MongoDB 21.15%
20 Cassandra 19.23%
21 Planetscale 19.23% <-- you
22 AlloyDB 17.31%
23 ClickHouse 17.31%
Pass a second argument to widen or narrow the window (default is 2 rows on each side):
./competitors-around-rank.sh a8bbcb6f-1ee3-462b-98f1-abe8e05e5437 5
If you’re ranked #1 (or near the bottom of the tracked list) in a topic, the window just clamps to whatever rows exist in that direction — you’ll see fewer neighbors on that side, not an error.
All topics, with competitors around your rank
Combines both scripts above: loop every topic concurrently (like the first script), but show the window of competitors around your rank in each one (like the one above) instead of just your own row. Same parallel-lookup structure — background jobs writing to per-topic temp files, replayed in original order — just with more rows per topic.
#!/usr/bin/env bash
set -euo pipefail
WINDOW="${1:-2}"
YOUR_PRODUCT=$(tpc auth whoami | awk -F': ' '/^Product:/{print $2}')
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
check_topic() {
local topic_id="$1" topic="$2"
local rows
rows=$(tpc analytics explore --metric sov --by competitor --topic "$topic_id" --top 100 --json \
| jq -r --arg name "$YOUR_PRODUCT" --argjson window "$WINDOW" '
.rows as $rows
| ($rows | map(.label) | index($name)) as $idx
| if $idx == null then empty else
$rows[(if $idx - $window < 0 then 0 else $idx - $window end):($idx + $window + 1)][]
| [.rank, .label, .sov, (if .label == $name then "you" else "" end)] | @tsv
end
')
echo "$topic"
if [ -z "$rows" ]; then
printf " %s not in top 100\n" "$YOUR_PRODUCT"
else
echo "$rows" | while IFS=$'\t' read -r rank label sov marker; do
if [ "$marker" = "you" ]; then
printf " %-4s %-20s %s%% <-- you\n" "$rank" "$label" "$sov"
else
printf " %-4s %-20s %s%%\n" "$rank" "$label" "$sov"
fi
done
fi
echo
}
topic_ids=()
while IFS=$'\t' read -r topic_id topic; do
topic_ids+=("$topic_id")
check_topic "$topic_id" "$topic" > "$tmpdir/$topic_id" &
done < <(tpc analytics explore --metric sov --by topic --json | jq -r '.rows[] | [.key, .label] | @tsv')
wait
for topic_id in "${topic_ids[@]}"; do
cat "$tmpdir/$topic_id"
done
Run it:
chmod +x competitive-position-with-window.sh
./competitive-position-with-window.sh
Pass a window size as the first argument (default 2):
./competitive-position-with-window.sh 5
Example output:
comparison
1 Planetscale 58.82% <-- you
2 Vitess 47.06%
3 MySQL 44.12%
feature
1 PostgreSQL 45.45%
2 Planetscale 42.42% <-- you
3 MySQL 42.42%
4 Google Cloud Spanner 33.33%
use-case
1 Amazon Aurora 38%
2 Redis 38%
3 Planetscale 36% <-- you
4 Google Cloud Spanner 34%
5 AWS 34%
budget
1 PostgreSQL 34.15%
2 Planetscale 34.15% <-- you
3 Google Cloud SQL 31.71%
4 Neon 31.71%
industry
19 MongoDB 21.15%
20 Cassandra 19.23%
21 Planetscale 19.23% <-- you
22 AlloyDB 17.31%
23 ClickHouse 17.31%
Firing one concurrent request per topic can occasionally trip the API’s rate limit (429 Too many requests) if you’re re-running the script repeatedly in quick succession, for example while iterating on it. Wait a few seconds and retry — it’s a rate limit on request volume, not a problem with your data or account.