> ## Documentation Index
> Fetch the complete documentation index at: https://docs.genviral.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Get Best Posting Times

> Rank the weekdays and hours when your own published posts performed best, from your Genviral analytics.

Ranks the weekday and hour slots where your own published posts did best. It reads the analytics of
the workspace (for a workspace key) or your personal space (for a personal key) and counts only
your own posts: posts from your connected accounts and posts published through Genviral. Accounts
you only track for research are left out. It never falls back to generic platform advice.

## Behavior

* Each published post is placed in a slot by the weekday and hour it went out, in the `timezone`
  you send.
* Slots are ranked mainly by average engagement rate, then by average views and by how many posts
  back them.
* When none of your own posts in the range has analytics, `status` is `insufficient_data` and
  `times` is empty. Try a longer `range`, or drop the `platform` filter.
* Read-only: nothing is scheduled and no credits are used.

## Query Parameters

<ParamField query="timezone" type="string" required>
  IANA time zone the slots are computed in, for example `Europe/Amsterdam`.
</ParamField>

<ParamField query="platform" type="string">
  Only rank posts from one platform: `tiktok`, `instagram`, `youtube`, `facebook`, `linkedin`, `x`,
  `threads`, `pinterest`, or `bluesky`. Omit it to rank all platforms together.
</ParamField>

<ParamField query="range" type="string" default="90d">
  How far back to look: `30d`, `90d`, or `180d`.
</ParamField>

<ParamField query="count" type="number" default="3">
  How many slots to return. Range: `1-5`.
</ParamField>

## Response

<ResponseField name="status" type="string">
  `ok` when at least one slot was ranked, otherwise `insufficient_data`.
</ResponseField>

<ResponseField name="range" type="string">
  The range that was analyzed.
</ResponseField>

<ResponseField name="platform" type="string | null">
  The platform filter, or `null` when all platforms were ranked together.
</ResponseField>

<ResponseField name="timezone" type="string">
  The time zone the slots are expressed in.
</ResponseField>

<ResponseField name="analyzed_post_count" type="number">
  Number of published posts the ranking was built from.
</ResponseField>

<ResponseField name="times" type="array">
  Best slots, best first. At most `count` items.

  <Expandable title="Time Object">
    <ResponseField name="rank" type="number">
      Position in the ranking, starting at `1`.
    </ResponseField>

    <ResponseField name="day_of_week" type="string">
      `monday` through `sunday`.
    </ResponseField>

    <ResponseField name="hour" type="number">
      Hour of the day in `timezone`, from `0` to `23`.
    </ResponseField>

    <ResponseField name="post_count" type="number">
      Number of posts published in this slot.
    </ResponseField>

    <ResponseField name="average_views" type="number">
      Average views of those posts.
    </ResponseField>

    <ResponseField name="average_engagement_rate" type="number">
      Average engagement rate of those posts, as a fraction (`0.081` is 8.1%).
    </ResponseField>

    <ResponseField name="views_vs_average" type="number | null">
      This slot's average views divided by the average views of all analyzed posts, rounded to
      two decimals. `1.67` means 67% more views than usual. `null` when the posts have no views
      to compare against.
    </ResponseField>

    <ResponseField name="explanation" type="string">
      One plain sentence explaining the slot.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```bash cURL theme={null}
  curl --request GET \
    --url 'https://www.genviral.io/api/partner/v1/analytics/best-times?timezone=Europe/Amsterdam&platform=tiktok&range=90d&count=3' \
    --header 'Authorization: Bearer <token>'
  ```
</RequestExample>

<ResponseExample>
  ```json Response theme={null}
  {
    "ok": true,
    "code": 200,
    "message": "Best posting times retrieved",
    "data": {
      "status": "ok",
      "range": "90d",
      "platform": "tiktok",
      "timezone": "Europe/Amsterdam",
      "analyzed_post_count": 42,
      "times": [
        {
          "rank": 1,
          "day_of_week": "tuesday",
          "hour": 18,
          "post_count": 6,
          "average_views": 15200,
          "average_engagement_rate": 0.081,
          "views_vs_average": 1.67,
          "explanation": "This slot averaged 8.1% engagement across 6 observed post(s)."
        },
        {
          "rank": 2,
          "day_of_week": "saturday",
          "hour": 11,
          "post_count": 4,
          "average_views": 9800,
          "average_engagement_rate": 0.064,
          "views_vs_average": 1.08,
          "explanation": "This slot averaged 6.4% engagement across 4 observed post(s)."
        }
      ]
    }
  }
  ```

  ```json Not enough data theme={null}
  {
    "ok": true,
    "code": 200,
    "message": "Best posting times retrieved",
    "data": {
      "status": "insufficient_data",
      "range": "30d",
      "platform": null,
      "timezone": "Europe/Amsterdam",
      "analyzed_post_count": 0,
      "times": []
    }
  }
  ```
</ResponseExample>

## Error Responses

* `401` - authentication failed (missing, invalid, or revoked token)
* `422 invalid_payload` - a query parameter is missing or invalid; `fields` names each one
* `500 best_times_failed` - unexpected error while reading analytics

All error responses from analytics endpoints include an `error_code` field.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.