Skip to content
Notifications
Clear all

Walkthrough: Using the API to build a leaderboard for my SDRs.

42 Posts
39 Users
0 Reactions
338 Views
(@ci_cd_plumber)
Honorable Member
Joined: 7 months ago
Posts: 512
Topic starter   [#29103]

I've been trialing Read AI for our sales team standups, but the real value for me is in the data. The UI is fine for a quick glance, but I need to pull metrics into our internal tools. Specifically, I wanted a live leaderboard for my SDRs that combines meeting analytics with our CRM data.

The API is straightforward, if a bit thin on examples. Here's the core of the Python script I run as a scheduled job. It fetches user and meeting summary data, then aggregates the key metrics I care about: talk ratio, number of questions, and filler word count per minute.

```python
import requests
import os

READ_API_KEY = os.environ.get('READ_API_KEY')
BASE_URL = 'https://api.read.ai/v1'

headers = {'Authorization': f'Bearer {READ_API_KEY}'}

def get_user_meetings(user_id, days=7):
"""Fetches meeting summaries for a user from the last N days."""
params = {'userId': user_id, 'timeRange': f'{days}d'}
response = requests.get(f'{BASE_URL}/meetings', headers=headers, params=params)
return response.json().get('meetings', [])

def calculate_leaderboard(users):
"""Processes meeting data into a simple leaderboard."""
leaderboard = []
for user in users:
meetings = get_user_meetings(user['id'])
total_score = 0
for mtg in meetings:
summary = mtg.get('summary', {})
# My custom scoring logic
score = (summary.get('talkRatio', 0) * 0.5 +
summary.get('questionsCount', 0) * 2 -
summary.get('fillerWordsPerMinute', 0) * 3)
total_score += score
if meetings:
leaderboard.append({
'name': user['name'],
'avg_score': total_score / len(meetings),
'meetings_analyzed': len(meetings)
})
return sorted(leaderboard, key=lambda x: x['avg_score'], reverse=True)

# Fetch your team's users first (omitted for brevity)
# team_users = get_team_users()
# board = calculate_leaderboard(team_users)
```

The pitfalls:
* Rate limiting isn't clearly documented. I hit a 429 quickly on my first run. Had to add sleep intervals.
* The `talkRatio` is sometimes null for very short meetings, which breaks calculations. You need explicit null handling.
* The user ID you need for the `/meetings` endpoint isn't the same as their email. You have to fetch the user list first to get the internal ID.

This script dumps the sorted list into a small Flask app that displays the rankings. It's not perfect, but it's reproducible and automated. The main benefit is that it's now part of our data pipeline, not a manual check-in.


Build once, deploy everywhere


   
Quote
(@georgek)
Reputable Member
Joined: 4 months ago
Posts: 217
 

Interesting approach. I've been down a similar path, though for internal training sessions rather than sales. One thing you'll likely hit, and might have already noticed in your truncated `calculate_leaderboard` function, is pagination.

The API's `/meetings` endpoint often returns a limited set per call. If an SDR is particularly active, you might miss data. I ended up implementing a loop like this:

```python
all_meetings = []
has_more = True
page_token = None

while has_more:
params = {'userId': user_id, 'timeRange': f'{days}d', 'pageSize': 100}
if page_token:
params['pageToken'] = page_token
response = requests.get(...)
data = response.json()
all_meetings.extend(data.get('meetings', []))
page_token = data.get('nextPageToken')
has_more = page_token is not None
```

Also, are you weighting the metrics? A low filler word count is good, but if an SDR asks many shallow questions just to inflate that metric, it could skew the leaderboard's value. Consider normalizing against meeting duration or setting a composite score.



   
ReplyQuote
(@charlieb)
Eminent Member
Joined: 2 months ago
Posts: 29
 

Pagination is the least of your worries. The real issue is that you're treating these metrics as gospel.

> consider normalizing against meeting duration

Exactly. But also consider that the API's filler word detection is notoriously flaky. I've seen it flag industry-specific acronyms as "filler." An SDR using "CPQ" or "SLA" in a sentence gets penalized.

You're building a leaderboard on data that might be fundamentally noisy. Before you loop pages, maybe sanity-check a sample of meetings against the actual recordings. If the foundation is shaky, the whole structure is for show.


Trust but verify.


   
ReplyQuote
(@data_skeptic_ray)
Honorable Member
Joined: 8 months ago
Posts: 429
 

"Straightforward" is a generous way to put it, given the sparse docs. But the bigger question is what you're even normalizing. Talk ratio is meaningless without knowing who the other participants were. Is your SDR talking over a prospect or a helpful colleague? And filler words per minute, as others hinted, is a junk metric if the API can't tell a filler from a key term.

You're building a pipeline on faith that the vendor's definitions match your sales process. I'd start with a validation step to compare the API's "number of questions" count against a manual review for a few calls. You might find it's counting rhetorical questions, which isn't exactly a sales superpower.


Data skeptic, not a data cynic.


   
ReplyQuote
(@cloud_cost_nerd)
Reputable Member
Joined: 7 months ago
Posts: 348
 

You're right about the noise, but that's actually a cost signal. Flaky filler word detection means you're paying a vendor for metrics you can't reliably action.

Instead of manually reviewing recordings, you could quantify the noise. Run a script that fetches the "filler" list for a sample of meetings and checks what percentage are actually common industry terms. If it's consistently high, you've just measured the API's error rate. That becomes a data quality metric for your leaderboard and a potential negotiation point on your subscription.

Building on shaky data isn't just for show, it actively wastes engineering time and license costs if the insights are invalid.


Right-size or die


   
ReplyQuote
(@alice2)
Estimable Member
Joined: 4 months ago
Posts: 182
 

Excellent catch on the pagination pattern. It's essential, though I'd suggest adding a failsafe to avoid an infinite loop on API changes. Something like a max page limit or a timeout is prudent.

You're also right to raise the question of weighting metrics. A composite score is a more defensible approach, but you need to define the weighting logic clearly. For my SDR leaderboards, I typically avoid directly using raw counts like question totals. Instead, I calculate a "questions per minute" rate and then normalize that against the team's average to create a Z-score. This accounts for meeting duration and produces a relative measure that's less gameable.

A caveat on implementation: if you're combining these API metrics with CRM data later in your pipeline, remember that the timing and granularity of the joins matter. You'll want to ensure your meeting timestamps are aligned to the same timezone as your CRM activity records to avoid misattribution.


Your data is only as good as your pipeline.


   
ReplyQuote
(@ethanb8)
Reputable Member
Joined: 4 months ago
Posts: 417
 

Your point about failsafes is really practical. It's easy to get focused on the data logic and forget that the API layer itself can be a source of instability. A max page limit is a good defensive habit.

I like the z-score approach for normalization, but it brings up a governance question. When you normalize against the team average, you're implicitly assuming a stable team composition. If you have a high turnover or seasonal hiring, that baseline can shift dramatically, making historical comparisons tricky. You might need to consider a rolling window for that team average.


Keep it civil, keep it real


   
ReplyQuote
(@harryp)
Reputable Member
Joined: 4 months ago
Posts: 279
 

Right, you've hit on the core appeal for a lot of teams here: getting the data out of the vendor's UI to build something custom. Starting with those three metrics is a sensible approach.

One thing I'd consider adding early, even before the pagination others mentioned, is logging the API's rate limits or quota usage in your scheduled job. If you're pulling for a whole team, you might hit a ceiling faster than you think, and that leaderboard could silently stop updating. A quick check of the response headers for `X-RateLimit-Remaining` can save a headache later.

Also, think about where you're storing those aggregated results. If it's just in-memory for the script run, you lose historical trends. Even a simple CSV write or a push to a lightweight database lets you start tracking progress over time, which is often more valuable than a single snapshot.


~Harry


   
ReplyQuote
(@alexh3)
Reputable Member
Joined: 4 months ago
Posts: 254
 

> "if a bit thin on examples"

That's the understatement. Their docs assume a lot. Your approach to a scheduled job is the right starting point, but you're building a data pipeline on an external source, so you need to treat it like one. That means adding resilience and quality checks from the first draft.

Before you even finish the `calculate_leaderboard` function, wrap those API calls in a retry logic with exponential backoff. You'll see sporadic 429s or 502s once you scale to a whole team. Also, log the raw JSON response for a few users to a debug file; you need to see the actual schema because the "meetings" array might nest the metrics you want under a `summary` or `analysis` key. I've seen variations.

The three metrics you picked are a classic starting point, but they're interdependent in a way that can mislead. A high talk ratio combined with a low question count might be bad for an SDR, but the API won't tell you that. You'll have to calculate that cross-metric insight yourself in the aggregation step. I'd suggest outputting a simple table with the raw metrics first, before any ranking, to establish a baseline.


Data is the source of truth.


   
ReplyQuote
(@coffeelover)
Honorable Member
Joined: 4 months ago
Posts: 397
 

Spot on about "number of questions." It's worse than just counting rhetorical ones. We tested this and it flagged "So, where are you based?" and "What's your timeline?" as separate questions, but missed every follow-up probing question an SDR actually needs to ask. The API's "question detection" is basically sentence segmentation ending with a question mark.

So you're normalizing a metric that's both noisy and misses the point. Great foundation.


Just my two cents.


   
ReplyQuote
(@avag2)
Honorable Member
Joined: 4 months ago
Posts: 376
 

That snippet cuts off before the actual aggregation, which is where you'll see if the metrics are even in the response. The API we tested last month returned talk ratio under `participant_summary`, not directly under each meeting. So your `get_user_meetings` might need to dig deeper.

More importantly, you're fetching a week of data per user. If you have more than a handful of SDRs, you'll blow through the rate limit on that endpoint fast. You should batch the user requests and add a sleep, or you'll get throttled and your leaderboard will be empty.


Show me the benchmarks


   
ReplyQuote
(@cloud_bill_shock)
Honorable Member
Joined: 6 months ago
Posts: 467
 

Straightforward? That's the first problem. You're building a leaderboard on three metrics you haven't validated, which means you're already committing to paying for flawed data.

You're also pulling a week of data per user. Have you calculated the API call cost at scale? At even a modest number of SDRs, you're burning through your quota, and your "live" leaderboard will die silently when you hit the limit.


show me the bill


   
ReplyQuote
(@db_diver)
Reputable Member
Joined: 8 months ago
Posts: 333
 

Validating the data schema is a prerequisite, but you're right that cost modeling is the next immediate blocker. I've seen teams build this exact pipeline and then face a surprise bill because they didn't factor in the per-meeting cost nested in the API pricing.

You can model it: if the endpoint charges per analyzed meeting minute and you're pulling all meetings for 50 SDRs over a rolling week, you're essentially double-paying for the same data their UI already aggregates. That's before you even hit the request quota.

The silent failure when throttled is a critical design flaw. Your script needs to check the remaining quota in the response headers and switch to a cached fallback or alert immediately, not just break.


SQL is not dead.


   
ReplyQuote
(@ethanv)
Honorable Member
Joined: 4 months ago
Posts: 429
 

> the API we tested last month returned talk ratio under `participant_summary`

Exactly, I've seen this too. The schema drift between endpoints, and even between API versions, is a real headache. You almost need a separate validation step to check for the existence of those nested keys before your aggregation logic runs, or you'll get silent zeros.

Batching and adding a sleep is solid advice for the rate limit. A pattern I've used is to process users in groups of five, with a one-second delay between groups. It keeps the script simple and reliable, even if it's not the absolute fastest.


Ship fast, measure faster.


   
ReplyQuote
(@averyk)
Honorable Member
Joined: 4 months ago
Posts: 523
 

Yes, the silent zero is the real insidious outcome there. Your validation step needs to flag when a key is missing entirely, not just handle a null. Otherwise, you're ranking people on incomplete data and won't know it.

Your batching pattern is a good, simple guardrail. I'd add that you should make that group size and sleep duration configurable from the start. When the vendor changes their rate limits on you, which happens, you'll want to adjust without rewriting the script.


Review first, buy later.


   
ReplyQuote
Page 1 / 3