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

# Analyze performance

> Pull engagement, reach, follower, and best-time data from the analytics endpoints — patterns for dashboards, exports, and BI ingestion.

PublishBuddy's analytics endpoints are designed to feed dashboards, data warehouses, and decision-making tools. This guide covers the most common workflows.

## Top-line workspace report

For a "how is the brand doing this month?" summary, hit the workspace analytics endpoint:

```bash theme={null}
curl "https://api.publishbuddy.com/v1/analytics/workspaces/$WORKSPACE_ID?from=2026-04-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

You'll get back a `WorkspaceAnalyticsResource` with cumulative metrics across every profile in the workspace — total impressions, total engagement, post count, follower growth, etc. — for the date range.

This is the natural target for "monthly brand report" automation: run it on the 1st of each month, store the JSON in your warehouse / BI tool, and you have a longitudinal record without any custom aggregation.

## Per-profile breakdown

When you want to compare profiles or drill into one network:

```bash theme={null}
curl "https://api.publishbuddy.com/v1/analytics/profiles/$PROFILE_ID?from=2026-04-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

To compare all profiles in parallel, list them first then fan out:

```javascript theme={null}
const { data: profiles } = await fetch(
  `https://api.publishbuddy.com/v1/workspaces/${workspaceId}/profiles`,
  { headers: { Authorization: `Bearer ${token}` } }
).then((r) => r.json());

const reports = await Promise.all(
  profiles.map((p) =>
    fetch(
      `https://api.publishbuddy.com/v1/analytics/profiles/${p.id}?from=${from}&to=${to}`,
      { headers: { Authorization: `Bearer ${token}` } }
    ).then((r) => r.json())
  )
);
```

## Time-series for charts

For a line chart of follower growth or engagement-over-time:

```bash theme={null}
curl "https://api.publishbuddy.com/v1/profiles/$PROFILE_ID/metrics?from=2026-01-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

Returns daily granularity over the date range. Feed it directly into Chart.js / Recharts / Plotly for a chart, or upsert into a `profile_metrics_daily` table in your warehouse for cross-quarter analysis.

## Find the best times to post

The best-times heatmap is computed from your own publishing history — it tells you *when your audience engaged most* given your own posting cadence, not a generic industry average.

```bash theme={null}
curl "https://api.publishbuddy.com/v1/analytics/best_times?profile=$PROFILE_ID&from=2026-01-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

The response is a 7×24 matrix — day of week × hour of day — with normalised engagement scores. Top use cases:

* **Schedule recommendation UI** — overlay the heatmap onto a calendar picker and bias users toward high-score slots.
* **Queue timeslot suggestions** — pre-fill a profile's recurring queue with the top N timeslots from the heatmap.
* **Audience timezone sanity checks** — see whether your highest-engagement hours line up with your assumed audience timezone.

You can query the heatmap for a single profile or for the whole workspace (aggregating across all profiles):

```bash theme={null}
curl "https://api.publishbuddy.com/v1/analytics/best_times?workspace=$WORKSPACE_ID&from=2026-01-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

## Hashtag insights

Two endpoints surface the hashtags that drove engagement:

```bash theme={null}
curl "https://api.publishbuddy.com/v1/workspaces/$WORKSPACE_ID/hashtags?from=2026-04-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"

curl "https://api.publishbuddy.com/v1/profiles/$PROFILE_ID/hashtags?from=2026-04-01&to=2026-04-30" \
  -H "Authorization: Bearer $PUBLISHBUDDY_TOKEN"
```

Each entry returns the hashtag string, how many posts used it in the date range, and aggregate engagement for those posts. Useful for building a "top hashtags" leaderboard or for spotting under-used high-performers.

## Pattern: nightly export to a warehouse

A common production setup is a nightly job that pulls yesterday's analytics into BigQuery / Snowflake / Postgres for BI.

```python theme={null}
from datetime import date, timedelta
import os, requests, json

yesterday = date.today() - timedelta(days=1)
window = {'from': str(yesterday), 'to': str(yesterday)}
headers = {'Authorization': f'Bearer {os.environ["PUBLISHBUDDY_TOKEN"]}'}

for workspace in requests.get(
    'https://api.publishbuddy.com/v1/workspaces', headers=headers
).json()['data']:
    ws_report = requests.get(
        f'https://api.publishbuddy.com/v1/analytics/workspaces/{workspace["id"]}',
        headers=headers, params=window,
    ).json()['data']

    profiles = requests.get(
        f'https://api.publishbuddy.com/v1/workspaces/{workspace["id"]}/profiles',
        headers=headers,
    ).json()['data']

    profile_reports = [
        requests.get(
            f'https://api.publishbuddy.com/v1/analytics/profiles/{p["id"]}',
            headers=headers, params=window,
        ).json()['data']
        for p in profiles
    ]

    upsert_to_warehouse(workspace['id'], yesterday, ws_report, profile_reports)
```

The endpoints are idempotent (no side effects), so you can re-run the job to backfill historical days if a previous run failed.

## Watch out for

<AccordionGroup>
  <Accordion title="Lag from the social networks" icon="hourglass-half">
    Metrics aren't real-time — they're as fresh as the last sync PublishBuddy did with the network's API. Instagram / Facebook lag by a few hours, TikTok / YouTube by up to 24 hours. If you ingest "yesterday" data, run the job late enough in the day for the lag to clear (e.g. UTC noon).
  </Accordion>

  <Accordion title="Date range size" icon="calendar-range">
    Very large date ranges (multi-year) return larger payloads and take longer. For warehouse ingestion, prefer many small daily pulls over one giant range pull — it's faster overall and easier to recover from partial failures.
  </Accordion>

  <Accordion title="Disconnected profiles" icon="link-slash">
    Analytics for a profile whose connection has expired will go stale until reconnection. The endpoint still responds with the most recent data PublishBuddy has on file, but no fresh sync will happen until the user reconnects in the dashboard.
  </Accordion>
</AccordionGroup>
