A campaign report starts as a list of links: the reel a creator posted for you, the carousel a partner published, a competitor's launch post. What you need next to each link is short: likes, comments, views, who posted it and when. Instagram shows all of it on the post's page, one page at a time.
This guide reads a list of post links and fills in those numbers with Python. Views take a second call, hidden like counts need care, and the account that published a collab is not always the one you expect; each gets its own section. Every response and output below is real, collected through ScrapingBot on November 10, 2026, from coffee posts by James Hoffmann, Lance Hedrick, Blue Bottle Coffee and others.
Before you start
- A ScrapingBot API key. Create a free account for 100 credits: 20 post lookups. No card needed.
- Python 3.9 or newer and
pip install requests. - The links of the posts and reels to report on, one per row in a CSV with a
urlcolumn.
Meta's API has no lookup by post link
Meta's Business Discovery reads another account's posts with like_count, comments_count and, for reels, view_count. It takes a username, not a link, and its media reference says it "can not be used to get data for media owned by personal Instagram accounts". The oEmbed endpoint does take a post link, and returns embed HTML with no counts. Insights are for your own posts (all checked November 2026).
When every creator in a campaign has a business or creator account and you know their usernames, Business Discovery is free and official: list each creator's recent media and match your links on permalink. For a personal account, or a pile of links whose owners you haven't looked up, the public page is what there is. /media/by_shortcode returns what that page shows a visitor, as JSON.
One post
Every Instagram call is a POST to https://scrapingbot.io/api/v1/instagram. The shortcode is the part of the link after /p/ or /reel/:
curl -X POST https://scrapingbot.io/api/v1/instagram \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endpoint": "/media/by_shortcode", "params": {"shortcode": "DdZOfSWiKJb"}}'
James Hoffmann's September 17 reel from a Lima coffee bar, trimmed to the fields a report needs:
{
"success": true,
"data": {
"id": "3988282667673625179",
"code": "DdZOfSWiKJb",
"media_type": 2,
"product_type": "clips",
"taken_at": 1789660448,
"like_count": 10457,
"comment_count": 255,
"view_count": null,
"like_and_view_counts_disabled": false,
"is_paid_partnership": false,
"sponsor_tags": null,
"coauthor_producers": [],
"caption": { "text": "I’m in Peru chasing a truly original specialty coffee experience. So naturally, I’ve ended…", … },
"owner": { "id": "971700", "username": "jimseven", "full_name": "James Hoffmann", "is_verified": true, "is_private": false, … },
"video_versions": [ { "height": 1280, "type": 101, "url": "https://scontent-iad3-1.cdninstagram.com/…" }, … ],
…
},
"duration": "0.82",
"statusCode": 200,
"creditsUsed": 5
}
| Field | What it is |
|---|---|
code, id | The shortcode from the link, and the numeric media ID. Key your records on the shortcode. |
like_count, comment_count | Likes and comments, exact. comment_count includes replies. |
product_type, media_type | clips is a reel. Otherwise media_type 1 is a photo and 8 a carousel (carousel_container, with carousel_media_count slides). |
taken_at | When it was posted, as a Unix timestamp (UTC). |
owner | The account that published it: id, username, full_name, is_verified. |
coauthor_producers | The other accounts on a collab post. Empty for most posts. |
is_paid_partnership, sponsor_tags | Instagram's paid partnership label, and the brand when one is tagged. |
like_and_view_counts_disabled | true when the owner hides like counts. Then like_count is not usable; see below. |
view_count | null on every reel we looked up. Views come from another call. |
The full answer is larger: 17 to 100 KB in our lookups, with the caption, image and video links (signed, and they expire) and, for a carousel, every slide. A lookup took 0.9 to 2.2 seconds and costs 5 credits.
Every link form holds the same shortcode
There is also /media/by_url, which takes the link itself. It is strict about the form of the link. We sent it eight:
Link, after instagram.com | What /media/by_url answered |
|---|---|
/reel/DdZOfSWiKJb/ | The reel |
/p/DdZOfSWiKJb/ | The same reel: /p/ works for reels too |
/jimseven/reel/ | The reel; the username and tracking parameters are ignored |
/reel/DdZOfSWiKJb, sent without https:// | The reel |
/p/DbLR0mzFuWj/ | The whole carousel, whichever slide the link points at |
/reels/DdZOfSWiKJb/ | 400 Invalid Instagram URL format. |
/tv/DdZOfSWiKJb/ | 400 Invalid Instagram URL format. |
/jimseven/ (a profile) | 400 Invalid Instagram URL format. |
The refusals are free, but /reels/ with an s is the address Instagram's own reels viewer shows, so it turns up in pasted lists. All of these forms carry the same shortcode. The script reads it out with one pattern and calls /media/by_shortcode, which then covers every one of them. Links that start instagram.com/share/ are different: the code in them is not a shortcode, so the script sets them aside and asks for the address the link opens.
Views come from the owner's reels list
The lookup has no views: view_count was null on all 11 reels we looked up, and no other field carried a play count. The number Instagram shows on a reel is in the owner's list of reels:
curl -X POST https://scrapingbot.io/api/v1/instagram \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"endpoint": "/reels/by_user_id", "params": {"user_id": "971700", "page_size": 12}}'
{
"success": true,
"data": {
"items": [
{ "media": { "code": "DdZOfSWiKJb", "taken_at": 1789660448, "play_count": 235093, "like_count": 10458, "comment_count": 256, … } },
…
],
"paging_info": { "max_id": "AQHTlrPNAjvg4NUGDj14WzBgBon7erloT217zpZr…", "more_available": true },
"status": "ok"
},
"duration": "4.36",
"statusCode": 200,
"creditsUsed": 5
}
user_id is the owner.id from the lookup. We listed 374 reels from 32 accounts this way, and every one had a play_count. What to know before relying on it:
- It is not guaranteed. In October this list often came back with the play counts empty: our profile and reels guide found them
nullon every NASA reel it read, and today NASA's twelve newest all had one. Instagram decides what it reports, so the script treats views as optional and writes "not reported" rather than a zero. - One call covers many reels. A page costs 5 credits whatever its size, so ask for more than the default 12. Time grows with the page: 12 reels took 4 to 8 seconds, 36 took 17 to 39, and 50 took 19 to 32 with one request timing out at 45 seconds (a
408, not charged). The script asks for 36. - Newest first. Older reels are on later pages, reached by sending
paging_info.max_idback asmax_id. A June reel from a creator who posts every other day was on the second page of 36. The script reads up toREEL_PAGESpages per owner and leaves views empty for reels it doesn't reach, rather than guessing. - The counts are exact, not rounded for display: Hoffmann's reel read 235,091 plays at 16:37 UTC and 235,093 at 16:53.
- Other endpoints can disagree.
/search/postsalso returns aplay_count. For three of the six reels we could compare, the two agreed to within a few dozen plays. For the other three the search result was higher: by 23% and 31%, and on one paid partnership reel 859,790 against 22,498, with the same 264 likes on both. Meta's own reference says itsview_count"includes both paid and organic metrics", so the larger number may count plays of the brand's ad, but neither answer says. Use one source throughout a report. This guide uses the reels list.
Hidden like counts come back as a small number
An account can hide the like counts on its posts. One reel in our list, from a creator who does, came back like this:
{
"code": "DcyYUWBIA02",
"product_type": "clips",
"like_and_view_counts_disabled": true,
"like_count": 3,
"comment_count": 533,
…
}
Three likes on a reel with 533 comments is not a real count. When like_and_view_counts_disabled is true, the script writes the likes as unknown instead of copying that number into a report. Comments still come through.
Collabs and paid partnerships
Two things a campaign report wants are in the same answer.
Who published it. A collab post appears on more than one profile, and owner is the account that created it. The reel DcgSPGrvr9U sits on Lance Hedrick's grid, but its owner is decentespressomachines, with Hedrick under coauthor_producers. If you match posts to the creators you paid by owner alone, that one goes missing; the script writes both.
Whether it is labelled. Noah Stern's June reel about a Bosch espresso machine carries Instagram's paid partnership label:
{
"code": "DZ-cbXoumTU",
"is_paid_partnership": true,
"sponsor_tags": [
{ "is_pending": false, "sponsor": { "id": "438318059", "username": "boschhomeus", "full_name": "Bosch Home Appliances US", … } }
],
"coauthor_producers": [ { "id": "438318059", "username": "boschhomeus", "is_verified": true, … } ],
"owner": { "id": "60524828106", "username": "noahsterncoffee", … },
…
}
The brand is not always named. Another creator's reel had is_paid_partnership: true, an #ad in the caption and sponsor_tags: null. Nor does every sponsored-looking post carry the label: of six posts with #ad in the caption that one keyword search returned, four were labelled and two were not. The flag tells you which posts Instagram itself marks, nothing more.
When a link is dead
We changed the last letter of a real shortcode and looked it up:
{
"success": false,
"error": "Instagram media was not found."
}
That is a 404, and it is not charged. The answer does not say why Instagram has nothing at that address, so a mistyped link and a removed post look the same from here. The script prints the row as gone and carries on with the rest.
The script
Save this as ig_post_stats.py. It reads posts.csv (or the file you name), looks every post up, then reads the reels list of each account that owns a reel on your list, and appends one row per post to instagram_posts.csv.
import csv
import os
import re
import sys
import time
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime, timezone
import requests
API = "https://scrapingbot.io/api/v1/instagram"
HEADERS = {"x-api-key": "YOUR_API_KEY"}
CONCURRENCY = 1 # your plan's limit: Free 1, Starter 10, Startup 50
REEL_PAGES = 2 # pages of 36 reels to read per owner when looking for a reel's views
OUT = "instagram_posts.csv"
FIELDS = ["checked", "shortcode", "type", "owner", "with", "posted", "likes", "comments",
"views", "paid", "sponsor", "caption", "url"]
KINDS = {1: "photo", 2: "video", 8: "carousel"}
credits = [] # one entry per successful call: 5 credits each
class Gone(Exception):
"""Instagram shows nothing at this address: a wrong link, or a post that was removed."""
def call(endpoint, params, tries=4):
"""One Instagram call: 5 credits on success. Failed calls, a 404 included, cost nothing."""
error = None
for attempt in range(tries):
try:
res = requests.post(API, headers=HEADERS, timeout=90,
json={"endpoint": endpoint, "params": params})
body = res.json()
except (requests.RequestException, ValueError) as err: # no answer, or not JSON
error = err
else:
if body.get("success"):
credits.append(5)
return body["data"]
if res.status_code == 404:
raise Gone(body.get("error"))
if res.status_code in (400, 401, 402): # retrying will not help
raise RuntimeError(f"{res.status_code} {body.get('error')}")
error = body.get("error")
time.sleep(2 ** attempt) # 408, 429, 5xx or a network error: wait and try again
raise RuntimeError(f"kept failing: {error}")
def shortcode(link):
"""The shortcode in a post link: /p/, /reel/, /reels/ or /tv/, with or without a username."""
if "/share/" in link:
raise ValueError("a share link: open it and copy the address it lands on")
m = re.search(r"instagram\.com/(?:[\w.]+/)?(?:p|reels?|tv)/([\w-]+)", link)
if not m:
raise ValueError("not a link to one Instagram post")
return m.group(1)
def stats(code):
"""One post or reel as a report row. Views are added later, from the owner's reels list."""
try:
d = call("/media/by_shortcode", {"shortcode": code})
except Gone as err:
return code, None, f"gone: {err}"
except RuntimeError as err:
return code, None, str(err)
hidden = d.get("like_and_view_counts_disabled") # the owner hides like counts
return code, {
"shortcode": code,
"type": "reel" if d.get("product_type") == "clips" else KINDS.get(d.get("media_type"), "?"),
"owner": d["owner"]["username"],
"owner_id": d["owner"]["id"],
"with": " ".join(c["username"] for c in d.get("coauthor_producers") or []),
"posted": datetime.fromtimestamp(d["taken_at"], timezone.utc).strftime("%Y-%m-%d %H:%M"),
"likes": "" if hidden else d.get("like_count"),
"comments": d.get("comment_count"),
"views": "",
"paid": "yes" if d.get("is_paid_partnership") else "",
"sponsor": " ".join(s["sponsor"]["username"] for s in d.get("sponsor_tags") or []),
"caption": " ".join(((d.get("caption") or {}).get("text") or "").split())[:80],
"url": f"https://www.instagram.com/p/{code}/",
}, "likes hidden" if hidden else ""
def cell(value):
"""Text that starts like a formula gets a leading ' so a spreadsheet shows it, not runs it."""
return "'" + value if isinstance(value, str) and value.startswith(("=", "+", "-", "@", "\t", "\r")) else value
def reel_views(job):
"""play_count for an owner's newest reels, read until every wanted reel has turned up."""
owner_id, wanted = job
plays, max_id = {}, None
try:
for _ in range(REEL_PAGES):
params = {"user_id": owner_id, "page_size": 36}
if max_id:
params["max_id"] = max_id
data = call("/reels/by_user_id", params)
for item in data.get("items") or []:
plays[item["media"]["code"]] = item["media"].get("play_count")
paging = data.get("paging_info") or {}
max_id = paging.get("max_id")
if wanted <= plays.keys() or not paging.get("more_available") or not max_id:
break
except (Gone, RuntimeError) as err:
print(f"reels of {owner_id}: {err}")
return plays
if __name__ == "__main__":
with open(sys.argv[1] if len(sys.argv) > 1 else "posts.csv", newline="", encoding="utf-8-sig") as f:
links = [r["url"].strip() for r in csv.DictReader(f) if (r.get("url") or "").strip()]
codes = []
for link in links:
try:
code = shortcode(link)
if code not in codes: # the same post twice is one lookup
codes.append(code)
except ValueError as err:
print(f"skipped {link}: {err}")
checked = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M")
with ThreadPoolExecutor(max_workers=CONCURRENCY) as pool:
results = list(pool.map(stats, codes))
wanted = {} # owner ID -> the reels of theirs on the list
for _, row, _ in results:
if row and row["type"] == "reel":
wanted.setdefault(row["owner_id"], set()).add(row["shortcode"])
plays = {}
for found in pool.map(reel_views, wanted.items()):
plays.update(found)
print(f"{checked} UTC: {len(codes)} posts, {sum(credits)} credits")
print(f"{'post':<13}{'type':<10}{'owner':<24}{'posted':<12}{'likes':>9}{'comments':>10}{'views':>11} notes")
rows = []
for code, row, note in results:
if row is None:
print(f"{code:<13}{note}")
continue
notes = [note] if note else []
if row["type"] == "reel":
if plays.get(code) is not None:
row["views"] = plays[code]
else:
notes.append("views: not reported" if code in plays else "views: not in the owner's newest reels")
if row["with"]:
notes.append("with " + row["with"])
if row["paid"]:
notes.append("paid partnership" + (": " + row["sponsor"] if row["sponsor"] else ""))
n = {k: f"{row[k]:,}" if row[k] != "" else "-" for k in ("likes", "comments", "views")}
print(f"{code:<13}{row['type']:<10}{row['owner']:<24}{row['posted'][:10]:<12}"
f"{n['likes']:>9}{n['comments']:>10}{n['views']:>11} {'; '.join(notes)}")
rows.append({**row, "checked": checked})
new = not os.path.exists(OUT)
with open(OUT, "a", newline="", encoding="utf-8") as f:
out = csv.DictWriter(f, fieldnames=FIELDS, extrasaction="ignore")
if new:
out.writeheader()
out.writerows({key: cell(value) for key, value in row.items()} for row in rows) # one row per post and run
print(f"added {len(rows)} rows to {OUT}")
- Set
CONCURRENCYto your plan. Lookups don't depend on each other, so they run side by side up to your plan's concurrency limit: 1 on the free plan, 10 on Starter. - One reels list per owner, however many of that owner's reels are on your list. A collab reel is listed under its owner; in the two collabs we checked it was in the co-author's list as well.
- Unknown stays empty. Hidden likes, views of a photo or carousel (Instagram shows none) and reels beyond
REEL_PAGESare left blank in the CSV, never written as 0. - A failure skips one post, not the run. Timeouts (
408), concurrency limits (429), server errors and dropped connections are retried with a growing pause. A post that still fails, or a402when the credits run out, becomes a line in the printout while the rest are saved. Failed calls are not charged. - No cell can run as a formula. Captions are written by strangers, and a spreadsheet treats a cell that starts with
=,+,-or@as a formula.cell()puts an apostrophe in front of such text so Excel or Sheets shows it instead; a caption that opens with an @mention gets one too.
Run it
A list the way people paste one: a reel link from the reels viewer, a carousel link pointing at a slide, a link with a username and tracking code, one post listed twice, one typo and one share link.
url
https://www.instagram.com/reel/DdZOfSWiKJb/
https://www.instagram.com/p/DbLR0mzFuWj/?img_index=1
https://www.instagram.com/p/DcTO39qFbGX/
https://www.instagram.com/lancehedrick/reel/Ddj3qyjN1c3/?igsh=MWQ1ZGUxMzBkMA==
https://www.instagram.com/reels/DcgSPGrvr9U/
https://www.instagram.com/p/DfVkfXvASwP/
https://www.instagram.com/reel/DfIfM-JS4EZ/
https://www.instagram.com/reel/DZ-cbXoumTU/
https://www.instagram.com/reel/DcyYUWBIA02/
https://www.instagram.com/p/DbLR0mzFuWX/
https://www.instagram.com/reel/DdZOfSWiKJb/?utm_source=ig_web_copy_link
https://www.instagram.com/share/reel/BAJqK0x9zQ
python ig_post_stats.py posts.csv
skipped https://www.instagram.com/share/reel/BAJqK0x9zQ: a share link: open it and copy the address it lands on
2026-11-10 16:53 UTC: 10 posts, 85 credits
post type owner posted likes comments views notes
DdZOfSWiKJb reel jimseven 2026-09-17 10,457 255 235,093
DbLR0mzFuWj carousel jimseven 2026-07-24 3,376 640 -
DcTO39qFbGX photo jimseven 2026-08-21 4,181 32 - with bristolcoffeefestival
Ddj3qyjN1c3 reel lancehedrick 2026-09-21 4,701 89 123,396
DcgSPGrvr9U reel decentespressomachines 2026-08-26 378 11 32,503 with lancehedrick
DfVkfXvASwP carousel bluebottle 2026-11-04 779 11 -
DfIfM-JS4EZ reel bluebottle 2026-10-30 1,165 7 46,746
DZ-cbXoumTU reel noahsterncoffee 2026-06-24 264 14 22,498 with boschhomeus; paid partnership: boschhomeus
DcyYUWBIA02 reel lifewithgracefullyglam 2026-09-02 - 533 - likes hidden; views: not in the owner's newest reels
DbLR0mzFuWX gone: Instagram media was not found.
added 9 rows to instagram_posts.csv
Twelve links, ten posts, nine rows. The duplicate cost nothing, the typo came back 404 for free, and the 85 credits went to 9 lookups and 8 pages of reels. Four of those pages went on two prolific creators: the June reel turned up on its owner's second page, and the reel with hidden likes, posted September 2 by an account that posts more than one reel a day, was not among its newest 72. Raise REEL_PAGES for accounts like that, or leave it and accept the gap.
The CSV holds the same rows with the caption's first 80 characters and a clean link:
checked,shortcode,type,owner,with,posted,likes,comments,views,paid,sponsor,caption,url
2026-11-10 16:53,DdZOfSWiKJb,reel,jimseven,,2026-09-17 15:54,10457,255,235093,,,"I’m in Peru chasing a truly original specialty coffee experience. So naturally, ",https://www.instagram.com/p/DdZOfSWiKJb/
2026-11-10 16:53,DZ-cbXoumTU,reel,noahsterncoffee,boschhomeus,2026-06-24 16:42,264,14,22498,yes,boschhomeus,"Making café-quality coffee doesn’t have to be so tedious, and @boschhomeus 800 S",https://www.instagram.com/p/DZ-cbXoumTU/
2026-11-10 16:53,DcyYUWBIA02,reel,lifewithgracefullyglam,,2026-09-02 13:51,,533,,,,"☕️ Nespresso lovers, this little Amazon find is SUCH a smart coffee hack 👀 These",https://www.instagram.com/p/DcyYUWBIA02/
…
Run it daily and the file becomes the history
Each run appends rows with a new checked time, so scheduling the script (cron, or Task Scheduler on Windows) builds a history without a database:
0 9 * * * cd ~/campaign && python3 ig_post_stats.py posts.csv >> report.log 2>&1
A dozen lines read that file back and print what changed between the first and the latest check of each post:
import csv
from collections import defaultdict
runs = defaultdict(list)
with open("instagram_posts.csv", newline="", encoding="utf-8") as f:
for row in csv.DictReader(f):
runs[row["shortcode"]].append(row)
for code, rows in runs.items():
first, last = rows[0], rows[-1]
change = [f"{key} {int(last[key]) - int(first[key]):+,}" for key in ("likes", "comments", "views")
if first[key] and last[key]]
print(f"{code:<13}{last['owner']:<24}{first['checked']} to {last['checked'][11:]} {', '.join(change)}")
Thirteen minutes after the run above, on the same list:
DdZOfSWiKJb jimseven 2026-11-10 16:53 to 17:06 likes +1, comments +1, views +0
DbLR0mzFuWj jimseven 2026-11-10 16:53 to 17:06 likes +0, comments +0
DfIfM-JS4EZ bluebottle 2026-11-10 16:53 to 17:06 likes +0, comments +0, views +2
DZ-cbXoumTU noahsterncoffee 2026-11-10 16:53 to 17:06 likes +0, comments +0, views +0
DcyYUWBIA02 lifewithgracefullyglam 2026-11-10 16:53 to 17:06 comments +0
…
One like and one comment on a two-month-old reel, two views on Blue Bottle's: small, but they register, because Instagram's counts come unrounded. (TikTok's don't.)
Cost and speed
- 5 credits per post, and 5 per page of an owner's reels when you want views. Thirty posts by ten creators is 200 credits a run; daily for a month is 6,000, about $1.09 on Starter ($49.99 for 275,000 credits). The 100 free credits cover 20 lookups.
- Dead links and failed calls are free. A
404, a400and a timeout cost nothing. - The reels pages set the pace. The run above took 2 minutes 49 seconds one call at a time, nearly all of it on the eight pages of 36 reels. With
CONCURRENCY = 10the same list took 36 seconds, and 54 on a second try.
Where to go from here
- What people said under a post: export its comments and replies to CSV.
- Vetting creators before the campaign: engagement rate for a list of creators and follower counts in bulk. All of an account's reels with their numbers: a profile and its reels.
- The TikTok half of the same campaign: track TikTok video views for a list of links.
- No code: the free Instagram profile stats tool, and for TikTok the engagement rate calculator and the sound tracker.
- Comparing providers? The best Instagram scraper APIs in 2026 covers cost and coverage. The post lookup reference lists every parameter, and the Instagram API page every endpoint.
Common questions
Is there an official API that returns an Instagram post's stats from its URL?
No. Meta's Business Discovery reads another account's posts with like_count, comments_count and, for reels, view_count, but it takes a username, not a post link, and only works for business and creator accounts. Meta's oEmbed endpoint takes a post link and returns embed HTML with no counts (checked November 2026). For any public post, POST {"endpoint": "/media/by_shortcode", "params": {"shortcode": "<shortcode>"}} to https://scrapingbot.io/api/v1/instagram.
How do I get the view count of an Instagram reel?
Not from the single-post lookup: view_count was null on all 11 reels we looked up. The number is in the owner's reels list. /reels/by_user_id returned a play_count for every one of the 374 reels we listed from 32 accounts on November 10, 2026. In October the same field often came back empty, so treat it as optional: the script in this guide reads 36 reels a call for 5 credits, matches them to your links by shortcode, and leaves views blank when Instagram reports none.
Why is the like count missing or tiny on some posts?
The owner hides it. Such posts carry like_and_view_counts_disabled: true, and the like_count that comes with them is not the real one: a reel with 533 comments came back with like_count 3. Treat likes as unknown whenever that flag is true. Comments, and views from the reels list, still come through.
Which Instagram link formats work?
/media/by_url accepted /p/…/ and /reel/…/ links, with or without a username in front, https:// or tracking parameters. It refused /reels/…/ (plural) and /tv/…/ with a 400. Every form holds the same shortcode, so the script reads it out and calls /media/by_shortcode, which covers all of them.
Can I tell whether a post is a paid partnership or a collab?
Yes. is_paid_partnership is true on posts that carry Instagram's paid partnership label, and sponsor_tags names the brand when the creator tagged one. coauthor_producers lists the other accounts on a collab post. Note that owner is whoever published it, which on a collab can be the brand rather than the creator.
How much does a daily report on 30 posts cost?
Five credits per post, plus five per creator for their reels list when you want views: 30 posts by 10 creators is 200 credits a run, 6,000 a month, about $1.09 on the Starter plan ($49.99 for 275,000 credits). The 100 free credits cover 20 lookups. Posts that come back 404 are free.