Is there a webhook for new Instagram or TikTok posts?
No. Instagram and TikTok send webhooks only to apps that an account owner authorized, and only about that account. For a public account you do not own, nobody will call you. Openhandle does not offer webhooks either. It reads when you ask.
The working pattern is a poll. Read the profile posts endpoint on a schedule, compare post IDs with what you stored, and treat every new ID as an event. One page per check is enough, because the feed is newest first.
Checked September 2026 · request and response run against the API · no affiliate links
curl "https://api.openhandle.dev/v1/instagram/profiles/@northstar_forge_test/posts?freshness=live" \
-H "Authorization: Bearer $OPENHANDLE_API_KEY"import { OpenHandle } from '@openhandle/sdk';
const openhandle = new OpenHandle({ apiKey: process.env.OPENHANDLE_API_KEY! });
// Run every 15 minutes. One page, newest first.
const page = await openhandle.instagram.profile('northstar_forge_test').posts.list({ freshness: 'live' });
for (const post of page.data) {
if (await seen(post.id)) break;
await onNewPost(post);
}{
"platform": "instagram",
"resource": "post",
"capturedAt": "2026-09-18T11:45:00Z",
"source": "live",
"data": [
{
"id": "910100000001",
"createdAt": "2026-08-09T15:30:00Z",
"metrics": { "likes": 840, "comments": 5 }
}
],
"meta": { "cursors": { "next": "b3BhcXVlLWN1cnNvcg" } }
}Trimmed to one post. Because the feed is newest first, you can stop at the first ID you already know. The example is a Test profile.
Pick a poll interval
| You need | Interval | Cost per account per day |
|---|---|---|
| Same-day | Every 6 hours | 4 live reads, $0.0025 each |
| Within the hour | Every 15 minutes | 96 live reads, $0.0025 each |
| A daily digest | Once a day | 1 read, $0.0005 on a cache hit or $0.0025 live |
Use freshness=live for a poll. A cache hit could return the same page you saw last time. The TikTok profile posts endpoint works the same way with the same shape.
Make it robust
- —Key on `id`, not position. Pinned posts sit at the top of an Instagram feed. A new post can appear below them. Compare IDs, do not assume the first item is newest.
- —Store `capturedAt`. It is when the platform showed the post. Your event time should be that, not your cron time.
- —Handle a private flip. An account can go private between polls.
PROFILE_PRIVATEis an event too. Store it and stop polling until you decide to retry. - —Spread the schedule. A thousand accounts polled at the same minute hit your own rate limit. Stagger them across the interval.
When this does not work
These answers are events. Store them, do not retry them.
| Situation | Status | Code |
|---|---|---|
| The account went private | 403 | PROFILE_PRIVATE |
| The account is deleted or renamed without an ID lookup | 404 | PROFILE_NOT_FOUND |
| You passed your own per-second limit | 429 | RATE_LIMITED |
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests. Retry after the Retry-After header.",
"requestId": "req_01j8…",
"retryable": true
}
}Rate limits are per key, 10 requests per second, and never billed. Honor Retry-After. Failed requests are never billed.
Read next
More Instagram questions
Send your agent ahead.
Paste the prompt into your agent and it sets everything up. Or come yourself, it takes five minutes.