Best Time to Post
One question, one endpoint: when should this account publish on this network?
Like /audience and /comments, the network is a
platform parameter, so the URL stays the same for every network.
Every answer says where it comes from (source) and how much to trust it
(confidence). If there is no data yet, best_times comes back empty. We never
fill it with made-up hours.
- Method:
GET - Endpoint:
/api/uploadposts/best-times - Authentication:
Authorization: Apikey <YOUR_API_KEY>(a user JWT or a white-label profile JWT also works)
Query Parameters
| Parameter | Type | Required | Description | Default |
|---|---|---|---|---|
platform | string | Yes | tiktok, instagram, facebook, youtube, linkedin, x, threads, pinterest, bluesky or reddit. | - |
profile_username | string | Yes | The profile's username. user and profile are aliases. | - |
timezone | string | No | IANA timezone for the hours in the answer, e.g. Europe/Madrid. | The profile's queue timezone, else UTC |
top | integer | No | How many hours to return in best_times (1-24). | 5 |
refresh | boolean | No | true recomputes instead of using the cached answer, but only if that answer is at least 24 hours old (YouTube and X: 7 days). | false |
page_urn | string | No | LinkedIn only: the organization ID or urn:li:organization:<id> of a Page this connection administers. Any other Page is a 400. Ignored on other networks. | The connection's Page |
facebook_page_id | string | No | Facebook only: the numeric id of one of the connection's Pages. A Page pinned to the profile always wins, as in uploads. Ignored on other networks. | The profile's Page |
Where the hours come from (source)
The endpoint uses the first source on this list that has enough data:
source | What it is | Networks | confidence |
|---|---|---|---|
audience_activity | The network reports how many followers are online each hour. | TikTok (Business connection), Instagram (accounts with 100+ followers that granted the insights permission) | high |
account_posts | We read the account's recent posts (last 180 days, older than 24 h) and compare each post's engagement with the account's median post. Needs at least 10 posts. | All except X | low under 30 posts, medium under 60, high at 60+. Always low if the posts cover fewer than 3 different hours. |
upload_post_history | Same method, using posts you published through Upload-Post whose analytics have been read. No call to the network. | All. The only source for X and for personal LinkedIn profiles | Same as above |
none | Not enough data yet. best_times and by_hour are empty. | - | null |
Some networks have no "followers online" metric in their API, so the hours come from the account's own posts:
- YouTube: the Analytics API has no hour dimension, only
dayandmonth. We use the Data API (views of your latest 50 uploads, 3 quota units) and cache the answer for 7 days. - Facebook: Meta deprecated
page_fans_onlinefor every API version on 2024-09-16. We use the Page's latest 100 posts (reactions + comments + shares). - LinkedIn: follower statistics are daily at best. For Pages we use the
latest 50 posts plus their share statistics. LinkedIn does not let apps list a
personal profile's posts, so personal profiles use
upload_post_history. - X: X charges for every post read, so best times for X only use posts you
published through Upload-Post whose analytics were read (
upload_post_history). - Threads: one insights call per post, for up to 25 recent posts.
- Pinterest, Bluesky, Reddit: engagement comes with the post list (pin metrics, likes/reposts/replies/quotes, score + comments).
- TikTok standard connection: no audience data, so we use the latest 60
videos. On a Business connection,
audience_activityis used. - Instagram:
online_followerscovers the last 30 days and needs 100+ followers. If the account has fewer followers, or the connection did not grant the insights permission, the answer falls back to the account's posts and adds awarningsentry. Reconnect Instagram to grant the permission.
Answers are cached for 24 hours (7 days for YouTube and X). The cache works in
UTC, so asking again with a different timezone does not cost another call.
Some networks share their API quota with publishing: YouTube (the same quota
your uploads use), Reddit, LinkedIn and Pinterest. Best times have their own
daily budget on those networks, so they can never use up the quota your posts
need. When that budget, or the network's quota, runs out for the day, the answer
uses only data we already have and says so in warnings. Fresh calculations
are also limited per account and day.
Example Request
curl -G "https://api.upload-post.com/api/uploadposts/best-times" \
-H "Authorization: Apikey YOUR_API_KEY" \
--data-urlencode "platform=instagram" \
--data-urlencode "profile_username=my_brand" \
--data-urlencode "timezone=Europe/Madrid" \
--data-urlencode "top=3"
Success Response 200 OK
{
"success": true,
"platform": "instagram",
"profile_username": "my_brand",
"timezone": "Europe/Madrid",
"source": "account_posts",
"source_detail": "instagram recent posts",
"confidence": "medium",
"best_times": [
{ "hour": 19, "score": 1.0, "relative_engagement": 1.84, "posts": 6 },
{ "hour": 13, "score": 0.81, "relative_engagement": 1.49, "posts": 5 },
{ "hour": 9, "score": 0.62, "relative_engagement": 1.14, "posts": 9 }
],
"by_hour": [
{ "hour": 0, "score": null, "relative_engagement": null, "posts": 0 },
"... 24 entries, hour 0 to 23 ..."
],
"by_day": [
{ "day_of_week": 0, "relative_engagement": 1.12, "posts": 7 },
"... 7 entries, 0 = Monday ..."
],
"by_day_hour": [[null, null, "... 24 per day ..."], "... 7 days ..."],
"sample": { "posts": 42, "min_posts": 10, "window_days": 180 },
"warnings": [
{
"code": "audience_permission_missing",
"message": "This Instagram connection did not grant the insights permission, so the hours come from your own posts. Reconnect Instagram from Manage Users to use when your followers are online."
}
],
"cached": false,
"computed_at": "2026-11-14T12:00:00+00:00",
"expires_at": "2026-11-15T12:00:00+00:00"
}
| Field | Description |
|---|---|
best_times | The best top hours, best first, in timezone. For post-based sources, only hours with at least one post. |
by_hour | 24 entries. score is 0-1 relative to the best hour, or null when nothing is known about that hour. For audience_activity, each entry has followers_online. For post-based sources, it has relative_engagement (1.0 = your median post, 2.0 = twice that) and posts (posts published at that hour). |
by_day | Post-based sources only: relative engagement per weekday (0 = Monday). |
by_day_hour | Post-based sources with 30+ posts: a 7×24 grid of relative engagement (null = no posts). |
sample | What the answer is based on: days (audience) or posts (post-based sources). |
warnings | Sources that could not be used, with a fixed code and a message that says what to do. |
Each post's engagement is capped at 5× your median post, and every hour is pulled toward 1.0 by one imaginary average post. A single viral post therefore cannot make an hour the best one by itself. An hour also counts half of the posts published in the hours on either side of it.
Errors
| Status | error_code | When |
|---|---|---|
400 | invalid_parameter | Missing platform, unknown timezone, top outside 1-24, malformed page_urn / facebook_page_id, or a Page this connection does not manage. |
400 | platform_not_supported | A network without best times. |
400 | account_not_connected | The profile has no account of that network. |
401 | reconnect_required | The network rejected the connection (HTTP 401 or 403; reauth_required: true). Reconnect it from Manage Users. |
429 | quota_exceeded | The network's API quota is used up for today and no other source has data. Try again tomorrow. |
429 | daily_limit_reached | Too many Page lookups for this account today (page_urn / facebook_page_id). |
502 | upstream_error | The network failed (error, timeout) and no other source has data. Try again later. |
503 | feature_disabled | Best times are temporarily unavailable. |
warnings[].code values: audience_permission_missing, audience_not_available,
audience_upstream_error, posts_upstream_error, posts_not_listable,
quota_exceeded, network_budget_exhausted, daily_limit_reached,
x_disabled, insufficient_history. Branch on the code; the message is for people.
Fill your queue with these hours
Set slots to "auto" in Queue settings
and the queue will use the best hours across the profile's networks.