# Open Graph Preview Card Generator — OG meta & social previews (`perryay/og-preview-generator`) Actor

Extract Open Graph meta tags (og:title, og:description, og:image, twitter:card) from any URL. Generate structured preview cards with validation warnings for missing or malformed OG tags. Batch mode for site-wide audits.

- **URL**: https://apify.com/perryay/og-preview-generator.md
- **Developed by:** [Perry AY](https://apify.com/perryay) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.015 / og tag extraction

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## OG Preview Generator 🖼️

**Stop guessing why your links look broken when shared on social media. The OG Preview Generator fetches any URL, extracts all Open Graph and Twitter Card meta tags, and delivers a structured validation report that tells you exactly what's missing, broken, or sub-optimal in your social preview setup — from missing `og:image` tags to titles that get truncated on LinkedIn, Facebook, and X/Twitter. Whether you're auditing a single blog post or running a site-wide scan of 50 pages, this actor gives you the actionable diagnostics you need to fix social preview issues before your audience ever sees a broken link.**

---

### What does it do?

Every time you paste a link into a social media post, chat message, or embed — Facebook, X/Twitter, LinkedIn, Slack, Discord, Telegram, and countless other platforms scrape that URL for Open Graph (`og:`) and Twitter Card meta tags to build the rich preview card users see. If those tags are missing, incomplete, or malformed, the result is an ugly, broken, or text-only link that kills engagement and looks unprofessional.

The OG Preview Generator automates the process of auditing your social preview metadata. You give it one URL or up to 50 URLs, and it fetches each page, parses every relevant `<meta>` tag, and returns a comprehensive breakdown. Beyond simple extraction, it validates what it finds: it flags missing required tags (`og:title`, `og:description`, `og:image`), recommends tags you should add (`og:url`, `og:type`, `og:site_name`), warns when titles or descriptions exceed platform truncation limits, and checks whether image URLs actually resolve. Every URL receives a social readiness classification — Full, OG Complete, Partial, or None — so you can instantly sort audited pages by how well they'll render when shared.

For batch runs (up to 50 URLs at once), the actor aggregates results into a summary: how many pages are fully social-ready, how many have partial OG support, and how many have no tags at all. This makes it ideal for site-wide audits, CMS migration validation, or CI/CD pipelines that gate deployments on social preview health.

Under the hood, the actor handles the details you shouldn't have to think about. It automatically follows HTTP redirects to reach the final page URL. It normalizes URLs that lack a scheme by prepending `https://`. It extracts the HTML `<title>` and `<meta name="description">` as fallback sources so the preview card can still provide useful data even when OG tags are absent. And because social scrapers use a dedicated User-Agent header (`OGPreviewBot/1.0`), the results reflect what platforms like Facebook and X/Twitter actually see — not what a human with a full-browser session would experience.

### Who is it for?

| Persona | What they use it for |
|---|---|
| **SEO Specialists** | Auditing every page on a domain to ensure social preview tags are present, correctly formatted, and optimized for click-through rates on social feeds. |
| **Content Marketers & Bloggers** | Checking blog posts and landing pages for complete OG/Twitter metadata before hitting publish — catching missing images or truncated descriptions before they go live. |
| **Web Developers & Frontend Engineers** | Integrating OG tag validation into build pipelines, PR checks, and deployment gates so no page ships without a valid social preview configuration. |
| **QA Engineers & Testers** | Automating social preview checks across staging and production environments as part of regression test suites and release validation workflows. |
| **Technical SEO Consultants** | Running competitive analysis on client domains versus competitor domains to compare social metadata quality and identify quick wins for SEO-led improvements. |
| **Platform & CMS Operators** | Verifying that custom CMS fields, head injection systems, or theme templates correctly output OG and Twitter tags across all template variants and content types. |
| **Digital Agency Teams** | Batch-auditing all client sites in a single run to produce standardized reports on social preview health, then tracking improvements over time. |
| **DevOps & Site Reliability Engineers** | Instrumenting automated monitoring that alerts when pages lose their OG tags after deploys, content migrations, or CDN configuration changes. |
| **E-commerce Merchandisers** | Ensuring product pages display correct title, description, and image when shared on social shopping platforms like Facebook Shops, Instagram, and Pinterest. |
| **Startup Founding Teams** | Running quick social preview checks on investor-facing landing pages, pitch decks, and product hunt launches before sharing links with audiences that matter.

### Why use this?

- **🛡️ Catch issues before they go live** — A missing `og:image` or an over-long title doesn't become visible until someone shares the link. By then, the broken preview is already cached on social platforms for days or weeks. The OG Preview Generator catches these issues before your audience ever sees them.

- **🔍 Goes beyond simple extraction** — Most OG tag tools just dump whatever meta tags they find. This actor validates: it checks title length (max 70 chars for social platforms), description length (max 200 chars), image URL resolvability, required versus optional tag coverage, and whether Twitter Cards complement your OG tags.

- **📊 Batch mode with aggregate intelligence** — Run up to 50 URLs in a single request and receive per-URL results plus a summary breakdown. You can immediately see that 14/50 pages are missing OG images, 3 have truncated titles, and 22 have no Twitter Card support — without inspecting each result individually.

- **🌐 Cross-platform diagnostics** — Social platforms each interpret OG and Twitter tags slightly differently. The actor returns both `og:` and `twitter:` namespaced tags, plus a combined preview card, so you can reason about how a link will render on Facebook (OG priority), X/Twitter (Twitter Card priority), LinkedIn (OG with its own truncation rules), Slack (custom preview logic), and Discord (OG with its own formatting).

- **⚙️ Simple API, powerful results** — One input field (`url` or `urls`), two optional parameters (`validateImage` and `timeout`). The output includes raw tags, a structured preview card, a validation report with actionable warnings, and a social readiness grade. Minimal config, maximum insight.

- **🧪 Verifiable image resolution** — When `validateImage` is enabled, the actor performs a HEAD request against every `og:image` URL it finds and reports HTTP status codes. A 404 or unreachable image URL is just as bad as a missing one, and this is the only reliable way to catch it at scale.

- **🏗️ CI/CD and automation ready** — The structured JSON output integrates naturally into GitHub Actions, GitLab CI, Jenkins, Datadog, Grafana, or any automation that consumes API results. Use it as a non-blocking advisory check or a hard gate that rejects deployments when social preview coverage drops below a threshold.

- **📦 Works with any stack** — The actor is a standalone HTTP API with no framework dependencies, database requirements, or runtime configuration beyond the input JSON. It works identically whether you call it from a Python script, a JavaScript fetch, a cURL command, or a Zapier webhook. No SDK installation needed.

- **🔄 Redirect-aware fetching** — The actor follows HTTP redirect chains (301, 302, 307, 308) to land on the final resolved URL. The `finalUrl` field in every result tells you where the page actually lives, catching redirect-based social preview issues that simple GET tools miss entirely.

- **📏 Deterministic, observable behavior** — Every run produces the same structured output schema. Timeout errors are returned as structured `error` fields in the results JSON — not as silent failures or dropped entries. Batch results always include a total count and a per-category summary for easy downstream processing.

### Features

1. **Full Open Graph Extraction** — Parses all standard OG properties: `og:title`, `og:description`, `og:image`, `og:url`, `og:type`, `og:site_name`, `og:locale`, `og:video`, `og:audio`, and `og:determiner`. Handles both `property=... content=...` and `content=... property=...` markup variations found across different CMS templates and HTML generators.

2. **Twitter Card Extraction** — Extracts all `twitter:` namespaced meta tags: `twitter:card`, `twitter:site`, `twitter:creator`, `twitter:title`, `twitter:description`, `twitter:image`, and `twitter:image:alt`. Twitter Cards complement OG tags and are the primary metadata source for X/Twitter previews; their presence alongside OG tags earns the highest social readiness classification.

3. **Smart Validation Engine** — Automatically checks for three required OG tags (`og:title`, `og:description`, `og:image`) and recommends three optional but valuable tags (`og:url`, `og:type`, `og:site_name`). Returns separate `missing` and `recommended` lists so you know what's critical versus nice-to-have.

4. **Title and Description Length Analysis** — Warns when `og:title` exceeds 70 characters (truncated on most social platforms) and when `og:description` exceeds 200 characters (trimmed on X/Twitter, LinkedIn, and Facebook feeds). Includes the actual length and a preview snippet in each warning.

5. **Image URL Validation** — Optionally (when `validateImage` is `true`) performs HTTP HEAD requests on every `og:image` URL to confirm it resolves with a 200 status. Reports per-image HTTP status codes and a boolean `valid` flag. Catches 404s, broken CDN links, expired S3 presigned URLs, and redirect chains that lead nowhere.

6. **Non-Absolute Image Detection** — Warns when `og:image` is a relative path or protocol-relative URL rather than an absolute HTTP/HTTPS URL — a common CMS misconfiguration that causes images to fail on external social scrapers.

7. **Social Readiness Classification** — Each analyzed URL receives one of four classifications:
   - **Full (OG + Twitter Card)** — All required OG tags present and Twitter Card metadata found
   - **OG Complete (Twitter Card missing)** — All required OG tags present, no Twitter Card tags
   - **Partial (missing required OG tags)** — Some OG tags found but at least one required tag missing
   - **None (no OG or Twitter tags detected)** — No relevant social meta tags found at all

8. **Structured Preview Card** — Builds a unified preview object from the best available data: `title` (prefers `og:title`, falls back to `<title>`), `description` (prefers `og:description`, falls back to meta `description`), `image` (from `og:image`), `url` (from `og:url` or the input URL), `siteName`, and `type`. This is the same shape many social media APIs return, making it easy to plug into existing preview-rendering components.

9. **Batch Auditing with Aggregate Statistics** — Analyze 2-50 URLs in a single run. Output includes per-object results plus a summary block: `socialReady` (count of full-ready pages), `partialOg` (partial coverage count), `noOg` (no tags count), and `errors` (fetch error count). The summary enables instant heat-map style assessment of site-wide social health without reading every individual result.

10. **Automatic URL Fixing** — If a URL is provided without a scheme (`example.com` instead of `https://example.com`), the actor automatically prepends `https://`. This reduces friction when pulling URLs from spreadsheets, sitemaps, or plain-text lists where the scheme is often omitted.

11. **Configurable Timeout** — Per-URL request timeout adjustable from 1 to 120 seconds. Useful for slow-loading pages, heavy SPA sites, or domains behind aggressive CDN caching layers. Defaults to 30 seconds — a good balance for most production sites.

12. **Error Isolation** — If a single URL in a batch fails to fetch (DNS error, timeout, connection refused), the actor isolates the failure: other URLs still process normally, and the failed entry returns a descriptive error message in its results with the URL clearly identified. The batch summary counts errors separately from successful analyses so you know at a glance how many pages need attention versus how many couldn't be reached.

13. **Dedicated Social Scraper User-Agent** — The actor identifies itself as `OGPreviewBot/1.0` when making HTTP requests, mimicking the user-agent used by social media scrapers from Facebook, X/Twitter, LinkedIn, and Slack. This means you see the same page content those platforms see — including any bot-targeted responses, blocking rules, or alternate content served to non-browser clients. Captcha pages, bot-block walls, and login redirects that wouldn't affect a human browser are surfaced honestly in the results.

### Input Parameters

| Field | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `url` | string | No* | — | A single URL to analyze. The actor follows redirects, auto-prepends `https://` if no scheme is present, and returns the full extraction result directly (not wrapped in a results array). Provide either this or `urls` (or both). |
| `urls` | array[string] | No* | `[]` | Array of URLs for batch analysis. Maximum 50 URLs per run. When this field is used, the output is wrapped in a `results` array with an additional `summary` object and `totalUrls` count. |
| `validateImage` | boolean | No | `false` | When `true`, performs a single HEAD request against each `og:image` URL found in the page results and reports the HTTP status code along with a boolean `valid` flag (whether the status was exactly 200). Useful for catching broken image links, missing CDN assets, or expired presigned URLs. Adds a small overhead per image-URL because of the extra HTTP request. |
| `timeout` | integer | No | `30` | Request timeout per URL in seconds. Minimum allowed: 1 second. Maximum allowed: 120 seconds. If a page takes longer than this to respond (including redirects), the actor returns the URL with an `error` field indicating a timeout. Increase for slow-loading pages with heavy JavaScript, or reduce for lightweight automation runs where fast failure is preferred. |

*\*At least one of `url` or `urls` must be provided. If both are supplied, both are analyzed (combined into a single batch list).*

### Example Input

The actor accepts either a single URL or a batch of URLs. The input is a simple JSON object with four possible fields. At minimum, you must provide content for at least one URL — either via the `url` field (single) or the `urls` array (batch). Both can be provided simultaneously; the actor merges them into a single batch internally.

#### Single URL

The simplest possible invocation — just a URL. The actor returns a single result object (not wrapped in an array). Default 30-second timeout applies.

```json
{
  "url": "https://example.com"
}
````

#### Single URL with image validation and extended timeout

For pages where you want to verify image URLs and need more time for page loading (e.g., media-heavy sites or SPA-backed pages), pass `validateImage: true` and increase the `timeout`:

```json
{
  "url": "https://example.com/blog/post-1",
  "validateImage": true,
  "timeout": 60
}
```

#### Batch (multiple URLs)

Process up to 50 URLs in a single run. The batch output includes per-URL results plus an aggregate summary. Image validation is applied to all URLs in the batch:

```json
{
  "urls": [
    "https://example.com",
    "https://example.com/about",
    "https://example.com/blog/post-1",
    "https://example.com/blog/post-2",
    "https://example.com/contact"
  ],
  "validateImage": true
}
```

#### Batch with mixed single + array (combined)

Both `url` and `urls` can be used together. The actor deduplicates and merges them into a single combined list:

```json
{
  "url": "https://example.com",
  "urls": [
    "https://example.com/about",
    "https://example.com/blog/post-1",
    "https://example.com/blog/post-2"
  ]
}
```

*When both `url` and `urls` are present, the actor treats them as a single combined batch — 4 URLs in this example. Duplicate URLs are not deduplicated; each entry is processed independently.*

### Output Format

The actor returns structured JSON. The shape depends on whether you submitted a single URL or a batch, but every individual URL result follows the same internal schema. Below are the possible response shapes.

#### Single URL response

When a single URL is provided via the `url` field (and `urls` is empty or absent), the actor returns the result object directly — not wrapped in an array. This keeps single-URL API responses as lightweight as possible for tools that only need to check one page.

```json
{
  "url": "https://example.com/blog/article-title",
  "finalUrl": "https://example.com/blog/article-title",
  "statusCode": 200,
  "pageTitle": "Article Title | Example Blog",
  "pageDescription": "An in-depth guide to building accessible web applications with modern CSS Grid and responsive design patterns.",
  "og": {
    "og:title": "Building Accessible Web Apps with CSS Grid",
    "og:description": "An in-depth guide to building accessible web applications with modern CSS Grid and responsive design patterns. Covers practical examples, accessibility considerations, and performance tips.",
    "og:image": "https://example.com/images/og-accessible-grid.png",
    "og:url": "https://example.com/blog/article-title",
    "og:type": "article",
    "og:site_name": "Example Blog",
    "og:locale": "en_US",
    "og:determiner": "",
    "og:video": "",
    "og:audio": ""
  },
  "twitter": {
    "twitter:card": "summary_large_image",
    "twitter:site": "@example",
    "twitter:creator": "@author_handle",
    "twitter:title": "Building Accessible Web Apps with CSS Grid",
    "twitter:description": "An in-depth guide to building accessible web applications with modern CSS Grid and responsive design patterns.",
    "twitter:image": "https://example.com/images/og-accessible-grid.png",
    "twitter:image:alt": "Diagram showing CSS Grid layout with accessible structure"
  },
  "otherMeta": {},
  "previewCard": {
    "title": "Building Accessible Web Apps with CSS Grid",
    "description": "An in-depth guide to building accessible web applications with modern CSS Grid and responsive design patterns. Covers practical examples, accessibility considerations, and performance tips.",
    "image": "https://example.com/images/og-accessible-grid.png",
    "url": "https://example.com/blog/article-title",
    "siteName": "Example Blog",
    "type": "article"
  },
  "validation": {
    "missing": [],
    "recommended": [],
    "warnings": []
  },
  "socialReady": "Full (OG + Twitter Card)"
}
```

#### Single URL response with validation failures

```json
{
  "url": "https://example.com/old-page",
  "finalUrl": "https://example.com/old-page",
  "statusCode": 200,
  "pageTitle": "Old Page - Example Website",
  "pageDescription": "",
  "og": {
    "og:title": "A Very Long Article Title That Definitely Exceeds Seventy Characters And Will Get Truncated On Most Social Platforms",
    "og:description": "This is a description that goes on for quite some time without really saying much because the CMS template just slaps the first 300 characters of body text into the meta description field regardless of platform limits.",
    "og:type": "website"
  },
  "twitter": {},
  "otherMeta": {},
  "previewCard": {
    "title": "A Very Long Article Title That Definitely Exceeds Seventy Characters And Will Get Truncated On Most Social Platforms",
    "description": "This is a description that goes on for quite some time without really saying much because the CMS template just slaps the first 300 characters of body text into the meta description field regardless of platform limits.",
    "image": "",
    "url": "https://example.com/old-page",
    "siteName": "",
    "type": "website"
  },
  "validation": {
    "missing": ["og:image"],
    "recommended": ["og:url", "og:site_name"],
    "warnings": [
      "og:title is 91 chars (recommended max: 70). Current: 'A Very Long Article Title That Definitely Exceeds Seventy Characters...'",
      "og:description is 308 chars (recommended max: 200)",
      "No og:image found — social previews will not show an image"
    ]
  },
  "socialReady": "Partial (missing required OG tags)"
}
```

#### Batch response structure

When two or more URLs are provided (via `urls` array, or by combining `url` + `urls`), the actor wraps per-URL results in a `results` array and includes a top-level `summary` block and `totalUrls` count. This is the only structural difference between single and batch output — every individual result object inside `results` has the exact same schema as the single-URL response described above.

```json
{
  "results": [
    { /* URL 1 result — same structure as single URL */ },
    { /* URL 2 result */ },
    { /* ... up to 50 results */ }
  ],
  "totalUrls": 5,
  "summary": {
    "socialReady": 2,
    "partialOg": 2,
    "noOg": 1,
    "errors": 0
  }
}
```

The `summary` object provides a quick overview of your batch's social health distribution:

- **`socialReady`**: Count of URLs classified as `Full (OG + Twitter Card)` — these pages are fully optimized for all major social platforms.
- **`partialOg`**: Count of URLs with partial OG coverage (missing at least one required tag or missing Twitter Cards).
- **`noOg`**: Count of URLs classified as `None` — pages with no OG or Twitter metadata whatsoever.
- **`errors`**: Count of URLs that could not be fetched due to network errors, timeouts, or DNS failures.

Note that `socialReady`, `partialOg`, and `noOg` only count successfully fetched URLs. URLs that failed with an error are counted exclusively in `errors` and do not affect the social readiness counts. This prevents a batch of mostly broken URLs from producing misleadingly "clean" readiness numbers.

#### With image validation enabled

When `validateImage` is set to `true` in the input, the actor goes beyond tag extraction and actively verifies that every `og:image` URL resolves correctly via an HTTP HEAD request. This catches a class of issues that simple extraction misses: images served from expired CDN URLs, assets behind broken S3 presigned links, images blocked by hotlink protection that return 403, or image URLs that have been removed but the OG tag was never updated.

Each result that has an `og:image` gains two additional fields:

```json
{
  "og:image:status": 200,
  "og:image:valid": true
}
```

If the image URL returns a 404, 403, or the HEAD request fails entirely (timeout, DNS error, connection refused):

```json
{
  "og:image:status": 404,
  "og:image:valid": false
}
```

Image validation adds minimal overhead — approximately one extra HEAD request per unique `og:image` URL found. For batch runs of 50 URLs where most pages have images, expect a modest increase in total run time proportional to the number of distinct image URLs discovered. The per-image timeout is fixed at 10 seconds, independent of the page-level `timeout` setting, so a single slow or hanging image URL cannot delay the entire batch beyond that limit.

> **Note**: Not all social platforms treat `og:image:valid=false` the same way. Some platforms fall back to a secondary image or show no image at all; others silently ignore validation failures and attempt the URL anyway. The actor provides the raw validation data so *you* can decide how to act on it.

#### Output field reference

| Field | Description |
|---|---|
| `url` | The input URL (as provided, before any auto-prefix). Useful for mapping results back to your original input list when processing batch results. |
| `finalUrl` | The final URL after following all HTTP redirects (301, 302, 307, 308). If `og:url` is present on the page, this reflects that instead of the redirect chain end — allowing comparison between canonical URL and actual page URL. |
| `statusCode` | HTTP status code from fetching the page (e.g., `200`, `301`, `404`, `500`). If the page redirects, this is the status code of the final response after the redirect chain. |
| `pageTitle` | Content of the HTML `<title>` tag — used as a fallback source for the preview card when `og:title` is absent or empty. |
| `pageDescription` | Content of the HTML `<meta name="description">` tag — used as a fallback source for the preview card when `og:description` is absent or empty. |
| `og` | Object containing all discovered `og:` properties as key-value pairs. Only properties that were actually found on the page appear as keys. Possible keys include `og:title`, `og:description`, `og:image`, `og:url`, `og:type`, `og:site_name`, `og:locale`, `og:video`, `og:audio`, `og:determiner`. |
| `twitter` | Object containing all discovered `twitter:` properties as key-value pairs. Only properties that were found appear as keys. Possible keys include `twitter:card`, `twitter:site`, `twitter:creator`, `twitter:title`, `twitter:description`, `twitter:image`, `twitter:image:alt`. |
| `otherMeta` | Reserved for future non-OG, non-Twitter meta extraction. Currently always returns `{}`. |
| `previewCard` | Unified preview object: `title`, `description`, `image`, `url`, `siteName`, `type`. Prefers OG data when available, falls back to HTML `<title>` and meta `description` tags for missing fields. |
| `validation.missing` | Array of required OG tags (`og:title`, `og:description`, `og:image`) that were not found on the page. Empty array means all required tags are present. |
| `validation.recommended` | Array of recommended OG tags (`og:url`, `og:type`, `og:site_name`) that were not found. These are beneficial for optimal preview rendering but not strictly required. |
| `validation.warnings` | Array of human-readable warning strings covering truncation risks, missing images, non-absolute URLs, and other issues. Each warning is self-contained and actionable. Common warnings include: title/description length exceeding platform limits, missing `og:image`, relative image URL detected, or no title found at all. |
| `socialReady` | One of four classification strings: `Full (OG + Twitter Card)`, `OG Complete (Twitter Card missing)`, `Partial (missing required OG tags)`, `None (no OG or Twitter tags detected)`. Can be used directly as a display label in dashboards and reports. |
| `error` | Present only when the page fetch failed entirely (timeout, DNS resolution failure, connection refused, SSL error). Contains a descriptive error message suitable for logging or display. When this field is present, `og`, `twitter`, `previewCard`, and `validation` will be empty or default. |
| `og:image:status` | Present only when `validateImage=true` and an `og:image` was found. Integer HTTP status code from the HEAD request to the image URL (e.g., `200`, `404`, `403`). |
| `og:image:valid` | Present only when `validateImage=true` and an `og:image` was found. Boolean — `true` only if the image HEAD request returned HTTP 200. `false` for any other status or if the HEAD request itself failed. |

### Use Cases

- **Social Media Audit** — Verify every page on your website renders correctly when shared on Facebook, X/Twitter, LinkedIn, Slack, Telegram, Discord, and any platform that consumes OG or Twitter Card metadata. Catch missing images, incorrect titles, and broken descriptions before they affect engagement.

- **Content Team QA Gate** — Automatically check that every new blog post, landing page, and marketing campaign page has complete OG tags before publishing. Integrate with your CMS workflow to flag posts that fail validation during the editorial review process.

- **Competitor Social Metadata Research** — Analyze how competitors configure social previews across their domains. Identify patterns — which tags they consistently include, what image dimensions they use, whether they leverage Twitter Cards alongside OG — and apply those insights to your own strategy.

- **SEO Monitoring Dashboard Integration** — Feed OG Preview Generator results into automated SEO dashboards (Grafana, Datadog, Google Looker Studio) to track social preview health over time. Set up scheduled runs to detect regressions: a page that was "Full" last week dropped to "Partial" after a CMS update? You'll know immediately.

- **Site Migration Validation** — After domain moves, CMS migrations (WordPress to Headless, Drupal to Next.js, etc.), or CDN reconfigurations, batch-verify that all OG metadata transferred correctly. Run the entire sitemap through the actor and compare results against a pre-migration baseline.

- **CI/CD Deployment Gate** — Add the OG Preview Generator to your GitHub Actions, GitLab CI, or Jenkins pipeline. Reject or flag deployments where the number of pages with "None" or "Partial" social readiness exceeds a configurable threshold. Prevents social preview regressions from reaching production.

- **Social Preview Cache Invalidation** — Use the actor's validation output to identify which URLs have changed OG tags and need social platform cache purging. Platforms like Facebook (via Sharing Debugger), LinkedIn (via Post Inspector), and X/Twitter (via Card Validator) all cache previews — knowing exactly which URLs changed lets you flush only what's necessary.

- **Template and Theming QA** — When developing or updating a site theme, run a representative set of page templates (homepage, blog post, category archive, product page, author bio) through the actor to confirm each template outputs correct OG tags. Catches template-level regressions before they propagate to all pages using that template.

- **Agency Reporting** — Produce standardized social preview health reports for client deliverables. The batch summary (`socialReady`, `partialOg`, `noOg`, `errors`) provides an at-a-glance site health score that clients can understand without diving into individual URL diagnostics.

- **Accessibility and Image Alt Auditing** — When `twitter:image:alt` is present in the results, you can audit whether social preview images include accessible alt text. Combining this with the `og:image` validation gives a complete picture of both functional and accessibility aspects of your social preview strategy.

- **Newsletter and Email Preview QA** — Email clients and newsletter platforms (Mailchimp, Substack, ConvertKit) increasingly use OG tags to render link previews inside email bodies. Run your newsletter URLs through the generator before each send to confirm titles, descriptions, and images render correctly across Gmail, Outlook, and Apple Mail.

- **Headless CMS Content Validation** — When using a headless CMS that separates content creation from presentation, it's easy for OG tags to drift between environments. Use the generator as part of your content staging workflow to check that every piece of content has the correct OG metadata before it goes from the headless API to production rendering.

- **SPA and Client-Side Rendered Page Auditing** — Single-page applications and frameworks (React, Vue, Angular) often struggle with OG tags because social scrapers may not execute JavaScript. Run your SPA URLs through the generator to see what the social scraper actually receives versus what a human sees in a browser, then fix SSR/SSG configurations accordingly.

# Actor input Schema

## `url` (type: `string`):

A single URL to extract Open Graph tags from. Alternative to 'urls' array.

## `urls` (type: `array`):

Array of URLs for batch OG tag extraction (max 50). Alternative to 'url' field.

## `validateImage` (type: `boolean`):

If true, performs HEAD requests on og:image URLs to verify they are accessible (charges additional event per URL)

## `timeout` (type: `integer`):

Timeout for each URL fetch (max 120)

## Actor input object example

```json
{
  "url": "https://example.com",
  "urls": [
    "https://example.com",
    "https://google.com"
  ],
  "validateImage": false,
  "timeout": 30
}
```

# Actor output Schema

## `results` (type: `string`):

OG tag analysis results in the default dataset — one item per URL analyzed

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "url": "https://example.com",
    "urls": [
        "https://example.com",
        "https://google.com"
    ],
    "timeout": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("perryay/og-preview-generator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "url": "https://example.com",
    "urls": [
        "https://example.com",
        "https://google.com",
    ],
    "timeout": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("perryay/og-preview-generator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "url": "https://example.com",
  "urls": [
    "https://example.com",
    "https://google.com"
  ],
  "timeout": 30
}' |
apify call perryay/og-preview-generator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=perryay/og-preview-generator",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Open Graph Preview Card Generator — OG meta & social previews",
        "description": "Extract Open Graph meta tags (og:title, og:description, og:image, twitter:card) from any URL. Generate structured preview cards with validation warnings for missing or malformed OG tags. Batch mode for site-wide audits.",
        "version": "1.0",
        "x-build-id": "RoethuipTWDNmmcAP"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/perryay~og-preview-generator/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-perryay-og-preview-generator",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/perryay~og-preview-generator/runs": {
            "post": {
                "operationId": "runs-sync-perryay-og-preview-generator",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/perryay~og-preview-generator/run-sync": {
            "post": {
                "operationId": "run-sync-perryay-og-preview-generator",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "url": {
                        "title": "Single URL",
                        "type": "string",
                        "description": "A single URL to extract Open Graph tags from. Alternative to 'urls' array."
                    },
                    "urls": {
                        "title": "Multiple URLs",
                        "type": "array",
                        "description": "Array of URLs for batch OG tag extraction (max 50). Alternative to 'url' field.",
                        "default": []
                    },
                    "validateImage": {
                        "title": "Validate OG Images",
                        "type": "boolean",
                        "description": "If true, performs HEAD requests on og:image URLs to verify they are accessible (charges additional event per URL)",
                        "default": false
                    },
                    "timeout": {
                        "title": "Request Timeout (seconds)",
                        "type": "integer",
                        "description": "Timeout for each URL fetch (max 120)",
                        "default": 30
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
