Insights
A channel's view velocity, ranked against channels its own size
/data-api/v2/insights/youtube/{handle}/benchmark- Scope
- insights:read
- Freshness
- catalog read
- Group
- Insights
- Platforms
- 1 of 7
What this endpoint answers
YouTube subscriber counts are rounded at the source and lifetime view counts are not, so views are the only exact series this platform gives us. Differencing them across a fortnight gives views per day, and this ranks that figure inside the percentile ladder for channels in the same subscriber band. It answers the question the activity endpoint deliberately leaves open: whether 1.8 million views a day is a lot for a channel this size.
This is a catalog read: it is served from the CRM Solid database in milliseconds, costs one budget unit, and reports how old the reading is in meta.cache_age_s. It needs the insights:read scope (Insights): Engagement rates, top posts, viral patterns, cohort benchmarks.
Good to know
- The floor is one million subscribers and it is a measurement, not a pricing decision. Daily-series coverage in the 30 days to 2026-08-31: every one of the 463 channels above 10M and all 4,425 between 1M and 10M had 14 or more observed days; of the 10,421 channels between 300k and 1M only 261 did; below 100k not a single channel had one. The crawler writes 5,000 to 8,000 rows a day against a 35,149 channel catalog and spends them at the head. A percentile from the 300k-1M band would rank a channel against the fifth of its peers that happen to be crawled, so the endpoint refuses instead, with placement.reason = below_coverage_floor.
- views_per_day is (last observed total - first observed total) / days spanned, not a mean of daily deltas. Two consecutive rows carrying an identical lifetime view count are common and mean the upstream returned a cached figure, not that the channel had a day with no views. Endpoint differencing is immune to that; averaging deltas is not.
- cohort.quantiles is a 101 point ladder, p0 through p100, and placement.percentile is read off it rather than interpolated between the five headline figures. cohort.measured is the ladder's real n; quote it rather than cohort.channels.
- The bottom of every ladder reads zero, and that is a real measurement rather than a missing one: 15 of the 463 channels above 10M subscribers (3.2%), 34 of 1,426 in 3M-10M (2.4%) and 81 of 2,999 in 1M-3M (2.7%) added no views at all across the 14 days to 2026-08-31. A channel that stopped is still a channel in the band, so it stays in the population being ranked against.
- The ladder is rebuilt at most every thirty minutes and ladder_computed_at says when. It moves slowly: the 10M+ median measured 1,772,605 views a day over a 14 day window and 1,731,396 over a 7 day one.
- views_per_day_per_1k_subscribers divides an exact number by a rounded one and inherits the rounding, which is exactly why the ladder is built on views_per_day instead. Use it to compare a channel with itself, not with another channel.
- views_per_video is a lifetime average over the whole back catalogue, so it is dominated by whatever the channel published years ago. It is a level, it is not part of the ladder, and it is not a measure of current performance.
- There is no subscriber growth endpoint for YouTube for the same reason: every subscriber count we hold is a multiple of ten. GET /insights/activity/platforms carries that measurement in full.
Parameters
Every value the call accepts, with the example the spec ships so the request runs as written.
| Name | In | Required | Example | What it does |
|---|---|---|---|---|
handle | path | required | veritasium | YouTube handle without the @, or a UC... channel id. |
Call it
Authenticate with a bearer token or the x-api-key header. Keys are server-to-server credentials. Never embed one in front-end code - call the API from your own backend and forward the result.
curl "https://crmsolid.com/data-api/v2/insights/youtube/veritasium/benchmark" \
-H "Authorization: Bearer psk_live_..."
const res = await fetch("https://crmsolid.com/data-api/v2/insights/youtube/veritasium/benchmark", {
headers: {
Authorization: `Bearer ${process.env.CRM_SOLID_DATA_API_KEY}`,
},
});
if (!res.ok) {
const { error } = await res.json();
throw new Error(`${error.code}: ${error.message} (${error.request_id})`);
}
const { data, meta } = await res.json();
import os
import requests
res = requests.get(
"https://crmsolid.com/data-api/v2/insights/youtube/veritasium/benchmark",
headers={"Authorization": f"Bearer {os.environ['CRM_SOLID_DATA_API_KEY']}"},
timeout=30,
)
res.raise_for_status()
payload = res.json()
data, meta = payload["data"], payload["meta"]
Keys look like psk_live_... for production keys, psk_test_... for test keys and are minted in the panel.
What comes back
Success is { data, meta }. Failure is { error: { code, message, request_id } }. The body below is the spec's own example: the values in it are illustrative readings, not live numbers.
- Channel id
- UCHnyfMqiRRG1u-2MsSQLbXA
- Handle
- veritasium
- Title
- Veritasium
- Status
- active
- Subscribers
- 21,200,000
- Subscribers precision
- rounded
- Hidden subscribers
- no
- Lifetime views
- 4,504,635,300
- Videos
- 515
- Views per video
- 8,746,864.7
- Published at
- 7/21/2010, 7:18:02 AM
- Window requested days
- 14
- Window from
- 2026-08-17
- Window to
- 2026-08-31
- Window days observed
- 15
- Window days spanned
- 15
- Window coverage pct
- 100
- Window longest gap days
- 0
- Velocity measured
- yes
- Velocity views per day
- 1,868,892.7
- Velocity views per day per 1k subscribers
- 88.15
- Velocity first day
- 2026-08-17
- Velocity first value
- 4,478,470,802
- Velocity latest day
- 2026-08-31
- Velocity latest value
- 4,504,635,300
- Cohort band
- 10m_plus
- Cohort label
- 10M+ subscribers
- Cohort min subscribers
- 10,000,000
{
"data": {
"channel_id": "UCHnyfMqiRRG1u-2MsSQLbXA",
"handle": "veritasium",
"title": "Veritasium",
"status": "active",
"subscribers": 21200000,
"subscribers_precision": "rounded",
"hidden_subscribers": false,
"lifetime_views": 4504635300,
"videos": 515,
"views_per_video": 8746864.7,
"published_at": "2010-07-21T07:18:02.000Z",
"window": {
"requested_days": 14,
"from": "2026-08-17",
"to": "2026-08-31",
"days_observed": 15,
"days_spanned": 15,
"coverage_pct": 100,
"longest_gap_days": 0
},
"velocity": {
"measured": true,
"reason": null,
"views_per_day": 1868892.7,
"views_per_day_per_1k_subscribers": 88.15,
"first": {
"day": "2026-08-17",
"value": 4478470802
},
"latest": {
"day": "2026-08-31",
"value": 4504635300
}
},
"cohort": {
"band": "10m_plus",
"label": "10M+ subscribers",
"min_subscribers": 10000000,
"max_subscribers": null,
"channels": 463,
"measured": 463,
"coverage_pct": 100,
"percentiles": {
"p10": 80274.7,
"p25": 547474,
"p50": 1772605.1,
"p75": 4170286.6,
"p90": 10412050.8
},
"quantiles": [
0,
0,
0,
0
]
},
"placement": {
"benchmarked": true,
"reason": null,
"percentile": 51,
"vs_median": 1.054,
"ladder_computed_at": "2026-08-31T14:20:11.402Z"
}
},
"meta": {
"request_id": "req_9f2c41a8b3d5",
"generated_at": "2026-08-23T09:14:02.317Z",
"took_ms": 42
}
}
The meta block
request_idstringrequiredUnique id for this request. Quote it in a support ticket.
generated_atstringrequiredServer time the response was produced.
took_msintegerrequiredMilliseconds spent server-side.
pagePageoptionalsourcestringoptionalWhich backend served the payload, for endpoints with more than one.
cache_age_sintegeroptionalAge of the underlying data in seconds. 0 for live reads.
When it fails
GET /insights/youtube/{handle}/benchmark documents 8 failure statuses. Branch on error.code, which is stable and enumerated; message is prose and may change.
- 401Unauthorized
unauthorized | invalid_keyNo key was presented, or the key is unknown, revoked or expired.
- 402PaymentRequired
payment_required | subscription_inactiveThe key is valid but the plan behind it cannot serve the call: the included requests are spent and overage is switched off, capped or unfunded (payment_required), or the billing period lapsed and was not renewed (subscription_inactive). Retrying does not help; paying does. The X-Plan-* headers on this response say how far past the line you are.
Carries X-Plan, X-Plan-Limit, X-Plan-Overage, X-Plan-Period-End, X-Plan-Remaining.
- 403Forbidden
forbidden_scope | forbidden_ipThe key is valid but not allowed to make this call: it lacks the scope, or the request came from an address outside the key's allowlist.
- 404NotFound
not_foundThe addressed resource does not exist.
- 422InvalidRequest
invalid_requestA parameter is malformed, out of range or mutually exclusive with another. `details` names the offending fields.
- 429RateLimited
rate_limited | quota_exceededEither the burst ceiling for the current minute or the daily quota is spent. Distinguish with the code: rate_limited clears within the minute, quota_exceeded does not clear until 00:00 UTC.
Carries Retry-After, X-Quota-Limit, X-Quota-Remaining, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-Request-Id.
- 500InternalError
internal_errorSomething failed on our side. Internals are never leaked; quote the request id.
- 503Unavailable
upstream_timeout | upstream_error | not_configuredThe request could not be served right now. BRANCH ON error.code, not on the status: 'upstream_timeout' means a source was too slow (this is what a catalog query hitting its 15-second statement timeout returns, so it is reachable from any endpoint that reads the corpus, not only the live-scrape ones) and the same call is worth retrying with backoff - narrowing it with a smaller limit, a filtered scope or a less popular account makes it far less likely; 'upstream_error' means a source was unreachable, so back off further; 'not_configured' means the capability has no backing service in this deployment, and retrying will never help.
Every response carries X-Request-Id and meta.request_id. Quote it in support requests.
Coverage and limits
This endpoint covers YouTube. Rate limits come from the tier on your key.
| Tier | Requests a minute | Requests a day | Live reads a minute |
|---|---|---|---|
| free | 30 | 1,000 | 5 |
| standard | 120 | 25,000 | 20 |
| pro | 600 | 250,000 | 60 |
| unlimited | 6,000 | 10,000,000 | 600 |
This call only draws on the ordinary per-minute and per-day columns. Every response reports where you stand in X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and the X-Plan headers.
Start calling it
A key takes a minute to mint in the panel, no card. The reference covers authentication, the envelope, scopes, rate limits and every error code in one page.
Reference path: /data-api/reference/insights-youtube-benchmark