# External posts

See posts your users published directly on a platform, outside Pozy, get webhooks when they appear, change or disappear, and import one on demand.

People do not post everything through your app. They post from the Instagram app, schedule in YouTube Studio or update their Google Business Profile in Google's own interface. Pozy picks those posts up too, so your analytics and content lists show the whole account, not only what went through the API. These are **external posts**.

## How Pozy finds them

A background sync reads each connected account's recent posts from the platform, roughly every 60 to 90 minutes, and compares them with what it already knows:

- A post Pozy has never seen is recorded and `post.external.created` fires.
- A post whose text or media changed fires `post.external.updated`. A platform rotating its own media URLs does not count as a change.
- A post that disappeared fires `post.external.deleted`, with the detection time in `deletedAt`.

Accounts with a listing API are covered: Instagram, Facebook, TikTok, YouTube, X, Threads, Pinterest, Bluesky, Google Business Profile and LinkedIn Pages. LinkedIn personal profiles have no listing API, so their posts only come in when you import them by URL (below).

> **The first sync is a backfill:**
> 
> Right after an account is connected, its existing posts are all reported as `post.external.created` once. Treat `created` as an upsert keyed on the platform post id, not as "something new was just posted".

## List external posts

`GET /v1/posts` returns posts created through Pozy by default. Pass `source=external` for the others:

```bash cURL
curl "https://api.pozy.ai/v1/posts?source=external&accountId=6710c3e2a9f14b0d8e5c7a22&limit=20" \
  -H "Authorization: Bearer $POZY_API_KEY"
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/posts?source=external&accountId=6710c3e2a9f14b0d8e5c7a22&limit=20", {
  headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.posts.length);
```
```python Python
resp = requests.get(
    "https://api.pozy.ai/v1/posts",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    params={"source": "external", "accountId": "6710c3e2a9f14b0d8e5c7a22", "limit": 20},
    timeout=30,
)
print(len(resp.json()["posts"]))
```

The analytics endpoints take `source` too, with `all` as the default, so reports include external posts unless you ask for `source=pozy`. See [Post analytics](https://pozy.ai/docs/analytics/post-analytics).

## Import one post right now

The background sync can take over an hour. When you need a post straight away, for example because a creator just pasted the link to the post they published for your campaign, call `POST /v1/posts/sync-external` with the account and the post's `url` (or its platform `postId`):

```bash cURL
curl -X POST https://api.pozy.ai/v1/posts/sync-external \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "accountId": "6710c3e2a9f14b0d8e5c7a22", "url": "https://www.instagram.com/reel/DBx7kQ2Nf3a/" }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/posts/sync-external", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ accountId: "6710c3e2a9f14b0d8e5c7a22", url: "https://www.instagram.com/reel/DBx7kQ2Nf3a/" }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.found, data.post?.analytics?.views);
```
```python Python
resp = requests.post(
    "https://api.pozy.ai/v1/posts/sync-external",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={"accountId": "6710c3e2a9f14b0d8e5c7a22", "url": "https://www.instagram.com/reel/DBx7kQ2Nf3a/"},
    timeout=60,
)
result = resp.json()
print(result["found"], (result.get("post") or {}).get("analytics"))
```

`found` says whether the post exists on that account, and `post` returns its content, media and current metrics. Pozy reads the account's latest posts live, so this works within seconds of publishing. Calls for the same account within about 15 seconds reuse the last read.

| Field | Notes |
| --- | --- |
| `accountId` | Required. A URL alone cannot be tied to an account, and Pozy reads the platform with that account's access. |
| `url` | Any post URL format the platform uses, including short links (`youtu.be`, `vm.tiktok.com`, `pin.it`). |
| `postId` | The platform's own id, as an alternative to `url`. |

For a LinkedIn personal profile, `url` is required and the post must be authored by the connected member. Imported LinkedIn posts carry metrics but no text or media, because LinkedIn does not share them for personal profiles.

An account that needs to be reconnected returns `409`. Reconnect it before retrying.

## What an external post looks like

External webhook payloads describe the post in the platform's terms:

```json
{
  "id": "5d2c8a40-7f1e-4c1b-9a3e-0b6f2d1e7c55",
  "event": "post.external.created",
  "timestamp": "2026-10-30T11:20:04.000Z",
  "profiles": [{ "id": "670f9b1d4c2e8a7f3b6d5e10", "externalId": "cust_4821" }],
  "account": { "id": "6710c3e2a9f14b0d8e5c7a22", "accountId": "6710c3e2a9f14b0d8e5c7a22", "platform": "instagram", "username": "acme.studio" },
  "post": {
    "id": "18045327754123456",
    "platform": "instagram",
    "accountId": "6710c3e2a9f14b0d8e5c7a22",
    "url": "https://www.instagram.com/reel/DBx7kQ2Nf3a/",
    "content": "Behind the scenes at the Porto workshop.",
    "mediaType": "VIDEO",
    "mediaItems": [{ "type": "video", "url": "https://scontent.cdninstagram.com/…/18045327754123456.mp4", "mediaStatus": "available" }],
    "thumbnailUrl": "https://scontent.cdninstagram.com/…/18045327754123456.jpg",
    "publishedAt": "2026-10-30T10:58:41.000Z",
    "source": "external",
    "deletedAt": null
  }
}
```

- `post.id` is the platform's own post id, not a Pozy post id. There is no Pozy post to update, retry or unpublish.
- `mediaItems[].mediaStatus` is `unavailable` when the platform withholds the file, for example for licensed music.
- External posts never carry your `metadata`, because Pozy did not create them.
- Deletion is detected from the platform's recent-posts listing, so removals of very old posts can go unnoticed.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/posts`](https://pozy.ai/docs/api/listPosts) | List posts |
| [`POST /v1/posts/sync-external`](https://pozy.ai/docs/api/syncExternalPosts) | Sync an external post |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`post.external.created`](https://pozy.ai/docs/webhooks/events#post-external-created) | External post created. |
| [`post.external.updated`](https://pozy.ai/docs/webhooks/events#post-external-updated) | External post updated. |
| [`post.external.deleted`](https://pozy.ai/docs/webhooks/events#post-external-deleted) | External post deleted. |
