# Link Preview API — OpenGraph & Metadata Unfurl Tool (`parsebird/link-preview-api`) Actor

High-volume OpenGraph, Twitter Card, and JSON-LD unfurl API. Extract title, description, image, canonical URL, favicon, feeds, hreflang, and schema.org data from any list of URLs.

- **URL**: https://apify.com/parsebird/link-preview-api.md
- **Developed by:** [ParseBird](https://apify.com/parsebird) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.69 / 1,000 links

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

### Link Preview API

Link Preview API unfurls any list of URLs into structured OpenGraph, Twitter Card, and JSON-LD metadata — the same data chat apps, feed readers, and CMSs use to render a rich link card — without running a browser.

<table><tr>
<td style="border-left:4px solid #1C1917;padding:12px 16px;font-weight:600">
Send up to 50 URLs at a time in parallel and get title, description, image, canonical URL, favicon, RSS/Atom feeds, hreflang alternates, and schema.org/JSON-LD data back per URL — with automatic retries and diagnostic records for anything unreachable.
</td>
</tr></table>

<br>

##### Copy to your AI assistant

```
Use the Apify Actor "parsebird/link-preview-api" (Link Preview API) to unfurl OpenGraph/Twitter Card/JSON-LD metadata from a list of URLs. Call it with the ApifyClient: `from apify_client import ApifyClient; client = ApifyClient("<APIFY_TOKEN>"); run = client.actor("parsebird/link-preview-api").call(run_input={"urls": ["github.com", "https://stripe.com"], "maxConcurrency": 10, "includeJsonLd": True})`. Key inputs: urls (array of URL strings or {"url": "..."} objects — a missing scheme is upgraded to HTTPS), maxItems (0 = process every URL), maxConcurrency (1-50, default 10), requestTimeoutSecs (3-120, default 20), maxRetries (0-3, default 1, retries timeouts/429/5xx), includeJsonLd (boolean, default true), includeRawTags (boolean, default false, dumps every meta tag into metaTags). Output is one JSON object per URL with url, finalUrl, ok, status, contentType, responseTimeMs, attempts, title, description, image, siteName, type, canonical, favicon, appleTouchIcon, manifestUrl, feedUrl/feeds, hreflang, language, charset, author, publisher, publishedTime/modifiedTime, robots, themeColor, twitterCard, openGraph, twitter, schemaTypes, jsonLd, and error (set when ok is false). Full API spec: https://apify.com/parsebird/link-preview-api/api. Get an API token at https://console.apify.com/account/integrations.
```

### What does Link Preview API do?

Link Preview API fetches each URL you give it and parses the response for every standard metadata signal a browser or bot would use to build a link preview — no headless Chrome, no rendering JavaScript, just the server-rendered `<head>` that OpenGraph/Twitter Card/JSON-LD were designed to be read from.

- 🔗 Accepts plain URL strings or `{"url": "..."}` objects, and auto-upgrades a missing scheme (`github.com` → `https://github.com`)
- ⚡ Up to **50 concurrent requests**, with per-URL timeout and automatic retries on timeouts, HTTP 429, and temporary 5xx errors
- 🏷️ Extracts OpenGraph (`openGraph`) and Twitter Card (`twitter`) properties, title, description, image, site name, and canonical URL
- 🧩 Parses **JSON-LD** blocks and detects `schema.org` types (`Article`, `Organization`, `Product`, and more)
- 🌐 Also pulls favicon, apple-touch-icon, web manifest, RSS/Atom feed links, and `hreflang` language alternates
- 🩺 Non-2xx responses, timeouts, and unreachable hosts come back as clean `ok: false` diagnostic records with an error message — and are **not charged**
- 📊 Runs on the Apify platform: schedule recurring runs, trigger via API or webhook, and export results as **JSON, CSV, Excel, or HTML**

### What data can you extract per URL?

| Field | Description |
|---|---|
| `ok` / `status` / `error` | Whether the fetch succeeded, the HTTP status, and the failure reason if not |
| `title` / `description` / `image` | The link-preview essentials (OpenGraph first, falling back to standard meta tags) |
| `siteName` / `type` / `canonical` | `og:site_name`, `og:type`, and the canonical URL |
| `favicon` / `appleTouchIcon` / `manifestUrl` | Icon and web app manifest links |
| `feedUrl` / `feeds` / `hreflang` | RSS/Atom feed links and language alternates |
| `author` / `publisher` / `publishedTime` / `modifiedTime` | Article metadata, from meta tags or JSON-LD |
| `openGraph` / `twitter` | Every `og:*` / `twitter:*` property found, as a flat object |
| `schemaTypes` / `jsonLd` | Detected `schema.org` `@type` values and the raw parsed JSON-LD blocks |
| `responseTimeMs` / `attempts` | Fetch latency and how many attempts it took |

### How to use Link Preview API

1. Open Link Preview API in the [Apify Console](https://console.apify.com) and click **Try for free**.
2. Paste your `urls` — one per line, or as an array via the API.
3. Optionally set `maxConcurrency`, `requestTimeoutSecs`, `maxRetries`, `includeJsonLd`, or `includeRawTags`.
4. Click **Start**. There's no per-URL rate limiting beyond what you configure — the run has no fixed timeout.
5. Download results as JSON, CSV, or Excel from the **Storage** tab, or pull them via the [Dataset API](https://docs.apify.com/api/v2#/reference/datasets).

### How much does it cost to use Link Preview API?

Link Preview API uses [pay-per-event pricing](https://docs.apify.com/platform/actors/publishing/monetize#pay-per-event-pricing) — you pay only for URLs successfully unfurled (`ok: true`). Diagnostic records for unreachable, timed-out, or non-success URLs are never charged.

| Plan | Price per link preview | Price per 1,000 |
|---|---|---|
| Free | $0.00099 | **$0.99** |
| Bronze | $0.00089 | **$0.89** |
| Silver | $0.00079 | **$0.79** |
| Gold | $0.00069 | **$0.69** |

Example: unfurling 10,000 URLs with a 95% success rate costs about $9.41 on the Free plan (9,500 billable previews). New Apify accounts include free platform usage credit that covers testing this Actor.

### Input / Output

```json
{
  "urls": ["github.com", "https://stripe.com", "https://www.apify.com"],
  "maxConcurrency": 10,
  "requestTimeoutSecs": 20,
  "maxRetries": 1,
  "includeJsonLd": true,
  "includeRawTags": false
}
```

Output — one JSON object per URL:

```json
{
  "url": "https://stripe.com",
  "finalUrl": "https://stripe.com/en-my",
  "ok": true,
  "status": 200,
  "contentType": "text/html; charset=utf-8",
  "responseTimeMs": 404,
  "attempts": 1,
  "title": "Stripe | Financial Infrastructure to Grow Your Revenue",
  "description": "Stripe is a financial services platform that helps all types of businesses accept payments, build flexible billing models and manage money movement.",
  "image": "https://images.stripeassets.com/fzn2n1nzq965/XtX984S1GJVsVOXFC7kMu/01988281e867728dfb09aa7793a6e3b9/Stripe.jpg",
  "type": "website",
  "canonical": "https://stripe.com/en-my",
  "schemaTypes": ["Organization", "WebSite"],
  "twitterCard": "summary_large_image"
}
```

Results can be downloaded as **JSON, CSV, Excel, or HTML** from the Storage tab, or fetched via the [Apify API](https://docs.apify.com/api/v2).

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("parsebird/link-preview-api").call(run_input={
    "urls": ["github.com", "https://stripe.com", "https://www.apify.com"],
    "maxConcurrency": 10,
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["url"], item["ok"], item.get("title"))
```

#### JavaScript

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

const client = new ApifyClient({ token: '<APIFY_TOKEN>' });
const run = await client.actor('parsebird/link-preview-api').call({
    urls: ['github.com', 'https://stripe.com', 'https://www.apify.com'],
    maxConcurrency: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Use cases

- **Chat/forum link cards** — generate the title/description/image for a rich link preview when a user pastes a URL.
- **Content audits** — run `includeRawTags: true` across a site's URL list to spot missing or malformed meta tags.
- **SEO monitoring** — track `title`, `description`, and `schemaTypes` for competitor pages over time.
- **Feed discovery** — bulk-resolve `feedUrl`/`feeds` for a list of blogs or news sites.
- **Link validation** — feed in a list of outbound links and use `ok`/`status`/`error` to find dead links at scale.

### Is it legal to use Link Preview API?

Yes. Link Preview API only reads the same publicly served `<head>` metadata — OpenGraph, Twitter Card, and JSON-LD tags — that every browser, chat app, and search engine already fetches to render a link preview. You're responsible for complying with each target site's terms of service and applicable data protection laws for your specific use case. See Apify's [blog post on the legality of web scraping](https://blog.apify.com/is-web-scraping-legal/) for more detail.

### Related Actors

- [HTTP Request Actor](https://apify.com/parsebird/http-request-actor) — call any REST API from your Apify workflow
- [Data Deduplicator](https://apify.com/parsebird/dataset-deduplicator) — remove duplicate URLs or records across runs
- [Data Cleaner](https://apify.com/parsebird/data-cleaner) — normalize and clean scraped datasets

### FAQ

**Why did some URLs come back with `ok: false`?**
A non-2xx HTTP response, a timeout, a DNS failure, or a site actively blocking automated requests all produce a clean `ok: false` record with an `error` message and, when available, a `status` code — instead of failing the whole run. These records are not charged.

**Does this render JavaScript?**
No. Link Preview API reads the server-rendered HTML directly, which is exactly what OpenGraph, Twitter Card, and JSON-LD tags are designed for — they exist specifically so bots and crawlers don't need to run JavaScript. A small number of sites that inject all metadata client-side via JS will return incomplete results.

**Can a site block this Actor?**
Some sites apply bot-detection to any non-browser traffic and may return a 403 or CAPTCHA challenge regardless of the request headers used. These come back as `ok: false` diagnostic records rather than crashing the run.

**What counts as a billable event?**
Only URLs that return `ok: true` — i.e., were successfully fetched and parsed. Diagnostic failures are free.

**Can I get every raw meta tag, not just the normalized fields?**
Yes — set `includeRawTags: true` to get a `metaTags` array with every `<meta>` tag's `name`/`property` and `content`.

**Can I schedule recurring runs?**
Yes. Use Apify's built-in [Scheduler](https://docs.apify.com/platform/schedules) to re-check a URL list daily, weekly, or at any interval, and trigger downstream automations via webhooks.

**Can I access this via API instead of the Console?**
Yes — every input field is available through the [Apify API](https://apify.com/parsebird/link-preview-api/api), with client libraries for Python, JavaScript/Node.js, and more.

**Found a bug or missing field?**
Please open an issue on the Actor's **Issues** tab — feedback is reviewed regularly and helps prioritize fixes.

# Actor input Schema

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

URLs to fetch and unfurl — plain strings, or { "url": "..." } objects when calling via the API. A missing scheme (e.g. "github.com") is upgraded to HTTPS automatically.

## `maxItems` (type: `integer`):

Safety cap on how many of the supplied URLs to process. 0 processes every supplied URL.

## `maxConcurrency` (type: `integer`):

How many URLs to fetch in parallel.

## `requestTimeoutSecs` (type: `integer`):

Per-URL timeout. The overall Actor run has no fixed timeout.

## `maxRetries` (type: `integer`):

Retries for timeouts, HTTP 429, and temporary 5xx errors.

## `includeJsonLd` (type: `boolean`):

Return parsed JSON-LD blocks and the detected schema.org types.

## `includeRawTags` (type: `boolean`):

Return every normalized meta tag in metaTags. Useful for audits, but increases output size.

## Actor input object example

```json
{
  "urls": [
    "github.com",
    "https://stripe.com",
    "https://www.apify.com"
  ],
  "maxItems": 0,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 20,
  "maxRetries": 1,
  "includeJsonLd": true,
  "includeRawTags": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

# 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 = {
    "urls": [
        "github.com",
        "https://stripe.com",
        "https://www.apify.com"
    ],
    "maxItems": 0,
    "maxConcurrency": 10,
    "requestTimeoutSecs": 20,
    "maxRetries": 1
};

// Run the Actor and wait for it to finish
const run = await client.actor("parsebird/link-preview-api").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 = {
    "urls": [
        "github.com",
        "https://stripe.com",
        "https://www.apify.com",
    ],
    "maxItems": 0,
    "maxConcurrency": 10,
    "requestTimeoutSecs": 20,
    "maxRetries": 1,
}

# Run the Actor and wait for it to finish
run = client.actor("parsebird/link-preview-api").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "urls": [
    "github.com",
    "https://stripe.com",
    "https://www.apify.com"
  ],
  "maxItems": 0,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 20,
  "maxRetries": 1
}' |
apify call parsebird/link-preview-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parsebird/link-preview-api"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/PzZ26zgOacPwmVuKC/builds/kpwrZHGLXEXgy6coc/openapi.json
