Insights
How often an account posts, and whether it has gone quiet
/data-api/v2/insights/instagram/{handle}/cadence- Scope
- insights:read
- Freshness
- catalog read
- Group
- Insights
- Platforms
- 1 of 7
What this endpoint answers
Posting rhythm measured from real post timestamps: how often, how regularly, how long since the last one, and whether that is normal for this account. This is the only account-level Instagram insight that works across the whole catalog, because it needs timestamps rather than a follower count.
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 rhythm figures come from the most recent 30 posts only, and window says exactly which ones. The crawl keeps the posts it has seen rather than a complete history, so an older stretch can contain long holes that are crawl artefacts and not quiet periods. primevideo holds 101 posts reaching back to 2021 with a 960-day hole in the middle, which is why no longest-gap figure is published.
- activity compares the account with its own median gap, not with a fixed number of days: active within twice the median gap, slowing within six times it, dormant beyond that, with floors of 3 and 14 days. activity_basis states the arithmetic on every response.
- profile_complete is false for 8,409 of the 10,947 catalog rows. Instagram's profile endpoint stopped answering anonymous callers, so those accounts reached us through the feed fallback: posts and timestamps arrive, follower count and bio do not. Cadence still works on them, which is the point of this endpoint; followers is null there and null means unknown, never zero.
- timing.by_hour_utc and by_weekday are returned only for accounts holding at least 20 posts, which is 568 of 10,918. Below that the block is null and timing.note says so rather than returning a histogram of six posts across 24 buckets.
- No best hour to post is named anywhere in this response. Which hour an account posts in is a count we can make; which hour earns more is a claim about engagement that 30 posts across 24 hours cannot support. The per-hour interaction averages are returned so a caller can look for themselves.
- data_age_s is the age of the crawl, not of the posts. Read it before trusting days_since_last_post: if the crawl last looked a week ago, the account may have posted since.
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 | nasa | Instagram handle, without the @. |
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/instagram/nasa/cadence" \
-H "Authorization: Bearer psk_live_..."
const res = await fetch("https://crmsolid.com/data-api/v2/insights/instagram/nasa/cadence", {
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/instagram/nasa/cadence",
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.
- Handle
- primevideo
- User id
- 1684102154
- Followers
- 7,003,795
- Profile complete
- yes
- Crawled at
- 8/29/2026, 2:56:49 PM
- Data age s
- 164,582
- Posts held
- 101
- Window from
- 8/26/2026, 2:00:06 PM
- Window to
- 8/30/2026, 5:00:08 PM
- Window posts
- 30
- Window span days
- 4.13
- Posts per week
- 49.21
- Gap hours p25
- 0.33
- Gap hours p50
- 0.99
- Gap hours p75
- 2.14
- Days since last post
- 0.82
- Activity
- active
- Activity basis
- Last post was 0.82 days ago against a median gap of 0.04 days across the last 30 posts.
- Timing sample
- 101
- Timing sufficient
- yes
- Timing busiest hour utc
- 17
- Timing busiest weekday
- 4
- Timing by hour utc
- 3 items
- Timing by weekday
- 1 items
- Timing note
- Hours are UTC. busiest_hour_utc is where this account posts most often, which is a count. No best hour is named: which …
{
"data": {
"handle": "primevideo",
"user_id": "1684102154",
"followers": 7003795,
"profile_complete": true,
"crawled_at": "2026-08-29T14:56:49.216Z",
"data_age_s": 164582,
"posts_held": 101,
"window": {
"from": "2026-08-26T14:00:06.000Z",
"to": "2026-08-30T17:00:08.000Z",
"posts": 30,
"span_days": 4.13
},
"posts_per_week": 49.21,
"gap_hours": {
"p25": 0.33,
"p50": 0.99,
"p75": 2.14
},
"days_since_last_post": 0.82,
"activity": "active",
"activity_basis": "Last post was 0.82 days ago against a median gap of 0.04 days across the last 30 posts.",
"timing": {
"sample": 101,
"sufficient": true,
"busiest_hour_utc": 17,
"busiest_weekday": 4,
"by_hour_utc": [
{
"hour_utc": 15,
"posts": 10,
"avg_interactions": 30993
},
{
"hour_utc": 17,
"posts": 17,
"avg_interactions": 25955
},
{
"hour_utc": 19,
"posts": 11,
"avg_interactions": 15036
}
],
"by_weekday": [
{
"weekday": 4,
"name": "Thursday",
"posts": 19,
"avg_interactions": 21408
}
],
"note": "Hours are UTC. busiest_hour_utc is where this account posts most often, which is a count. No best hour is named: which hour earns more is a claim about engagement and this sample cannot support it."
}
},
"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/instagram/{handle}/cadence 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 Instagram. 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-instagram-cadence