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

# Social oEmbed

> Resolve public social posts with a scoped API key.

`POST /api/v1/oembed/resolve` returns normalized metadata and available embed
markup for X, Facebook, Instagram, Threads, TikTok, and Reddit.

## Authentication

Use an issued Pirehub API key with the `oembed:read` scope. If you need a key or
additional permissions, contact [Pirehub support](mailto:support@ntanduy.com).
Keep the key on your server in an environment variable such as `PIREHUB_API_KEY`.

Send the key in `x-pirehub-api-key` or `Authorization: Bearer`. OAuth clients may
also request `oembed:read`; see [Authentication](/api-reference/authentication).

## Request

The body accepts only `url`: a nonempty HTTP(S) URL of at most 2048 characters.
Whitespace is trimmed. URLs with embedded credentials, non-default ports, private
network destinations, or unsupported provider hosts are rejected. URLs normalize
to HTTPS, and fragments and provider tracking parameters are removed.

```bash theme={null}
curl --request POST \
  --url https://www.ntanduy.com/api/v1/oembed/resolve \
  --header 'Content-Type: application/json' \
  --header "x-pirehub-api-key: $PIREHUB_API_KEY" \
  --data '{"url":"https://x.com/USERNAME/status/POST_ID"}'
```

Replace `USERNAME` and `POST_ID` with a public post. These templates are
illustrative; they do not reference real posts.

| Provider | URL template |
| - | - |
| X / Twitter | `https://x.com/USERNAME/status/POST_ID` |
| Facebook | `https://www.facebook.com/PAGE/posts/POST_ID` |
| Instagram | `https://www.instagram.com/p/POST_CODE/` |
| Threads | `https://www.threads.com/@USERNAME/post/POST_CODE` |
| TikTok | `https://www.tiktok.com/@USERNAME/video/VIDEO_ID` |
| Reddit | `https://www.reddit.com/r/COMMUNITY/comments/POST_ID/TITLE/` |

Supported provider short links include Facebook share links and `fb.watch`,
TikTok `vm.tiktok.com` / `vt.tiktok.com`, and `redd.it`. Redirect destinations
must remain within the provider's allowed hosts. Profile and arbitrary website
URLs are not supported. Public content can still be unavailable to the provider API.

## Response

This illustrative X response shows a post without a thumbnail:

```json theme={null}
{
  "success": true,
  "data": {
    "provider": "x",
    "type": "rich",
    "originalUrl": "https://x.com/USERNAME/status/123456789",
    "resolvedUrl": "https://x.com/i/status/123456789",
    "title": "Example public post text.",
    "author": { "name": "Example author", "url": "https://x.com/USERNAME" },
    "providerInfo": { "name": "X", "url": "https://x.com" },
    "thumbnail": null,
    "embed": { "html": null, "width": null, "height": null, "scripts": [] },
    "capabilities": {
      "html": false,
      "thumbnail": false,
      "nativeEmbed": false,
      "requiresHydration": false
    }
  }
}
```

| Field | Meaning |
| - | - |
| `provider` | `x`, `facebook`, `instagram`, `threads`, `tiktok`, or `reddit` |
| `type` | `rich`, `video`, `photo`, or `link` |
| `originalUrl` | Submitted URL after initial normalization |
| `resolvedUrl` | Provider URL after redirect resolution and canonicalization |
| `title` | Post text or title, or `null` |
| `author` | Author `name` and `url`; either can be `null` |
| `providerInfo` | Provider `name` and nullable `url` |
| `thumbnail` | `null`, or an image `url` with nullable `width` and `height` |
| `embed` | Nullable `html`, `width`, `height`, and a `scripts` array of `src` with optional `async` / `defer` |
| `capabilities` | Booleans for HTML, thumbnail, native embedding, and required script hydration |

X currently returns metadata without HTML or scripts. Other providers return
their available markup with script tags removed and the required script URLs
listed separately in `embed.scripts`. Raw provider metadata is not exposed.

## Render an embed

Check `capabilities.html` and `embed.html` before rendering. Provider markup is
untrusted: use a sandboxed iframe isolated from your application's origin,
and load approved provider scripts inside that frame when
`capabilities.requiresHydration` is true. Do not insert this HTML directly into
your application with `innerHTML` or `v-html`.

When HTML is unavailable, display the returned title, author, thumbnail, or link.
To generate a PNG, use the [screenshot API](/guides/screenshot) with a key that
also has `screenshot:capture`.

## Cache and limits

Successful results may be cached for one hour. Unavailable, private, or unsupported
post results may be cached for 60 seconds; provider rate-limit errors for 10 seconds.
The response does not include a cache-hit flag. This endpoint does not accept
`refreshCache` or other screenshot options.

Requests, including cache hits, use the API key's per-minute and monthly limits.
Inspect `X-ApiKey-RateLimit-*`, `X-ApiKey-Monthly-*`, and `Retry-After` when present.
Provider requests use an 8-second timeout within a 12-second resolution abort
signal; URL/DNS validation has separate bounds. These are not a guaranteed total
HTTP response time.

## Errors

Errors use `success: false`, `error.code`, `error.message`, and `data.requestId`.

| HTTP | Code | Meaning |
| - | - | - |
| `400` | `VALIDATION_ERROR` | Missing URL, invalid body, or additional fields |
| `400` | `OEMBED_INVALID_URL` / `OEMBED_SECURITY_REJECTED` | Invalid URL or rejected host/network destination |
| `401` | `UNAUTHORIZED` | Invalid API credential |
| `403` | `FORBIDDEN` | Revoked, expired, or insufficiently scoped credential |
| `403` | `OEMBED_PRIVATE_CONTENT` | Provider denied access to the content |
| `404` | `OEMBED_NOT_FOUND` | Post unavailable or removed |
| `422` | `OEMBED_UNSUPPORTED_PROVIDER` / `OEMBED_UNSUPPORTED_URL` | Provider or URL format unsupported |
| `422` | `OEMBED_URL_UNRESOLVED` | Short link could not resolve to a supported post |
| `429` | `RATE_LIMITED` / `MONTHLY_QUOTA_EXCEEDED` | API limit reached |
| `429` | `OEMBED_RATE_LIMITED` | Provider rate limit reached |
| `502` | `OEMBED_UPSTREAM_ERROR` / `OEMBED_INVALID_RESPONSE` | Provider failed or returned unusable data |
| `504` | `OEMBED_UPSTREAM_TIMEOUT` | Provider request timed out |

See [Errors and retries](/api-reference/errors) and the
[endpoint reference](/api-reference/endpoint/oembed-resolve) for the full contract.
