# URL Metadata API — OpenGraph, Twitter Card, JSON-LD, canonical (`alxlin/url-metadata`) Actor

Turn any list of URLs into structured page metadata JSON: title, description, canonical, OpenGraph, Twitter Card, JSON-LD types, favicon, language. HTTP-only, no login, no personal data. Pay per URL.

- **URL**: https://apify.com/alxlin/url-metadata.md
- **Developed by:** [Alex Lin](https://apify.com/alxlin) (community)
- **Categories:** Developer tools, SEO tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.80 / 1,000 url processeds

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

Turn any list of URLs into structured page metadata: title, description, canonical URL, OpenGraph, Twitter
Card, JSON-LD `@type`s, favicon, and language — one JSON item per URL, no browser required. Built for link
previews, content pipelines, SEO audits, and AI agents that need a plain `get_page_metadata(url)` tool. Every
URL is HTTP-only (`fetch`, no headless browser), so runs are fast and cheap, and one bad URL never fails the
whole run — it just comes back with an `error` field.

### Sample output

Real output from a live run of this Actor (`https://apify.com`):

```json
{
  "url": "https://apify.com",
  "finalUrl": "https://apify.com/",
  "status": 200,
  "title": "Apify: The largest marketplace of trusted tools for AI",
  "description": "Thousands of tools to automate your business. Get real-time web data, track competitors, generate leads, and integrate your apps and AI agents.",
  "canonical": "https://apify.com/",
  "lang": "en",
  "og": {
    "title": "Apify: The largest marketplace of trusted tools for AI",
    "type": "website",
    "site_name": "Apify",
    "image": "https://apify.com/og-image?title=Thousands+of+tools%0Afor+your+AI"
  },
  "twitter": {
    "card": "summary_large_image",
    "creator": "@apify"
  },
  "jsonLdTypes": ["Organization", "WebSite", "SoftwareApplication"],
  "favicon": "https://apify.com/favicon.ico?favicon.2653ilt32bing.ico?dpl=ff1ad5e33f",
  "error": null
}
```

A URL that 404s or isn't HTML still comes back as one item, with whatever was actually recoverable and an
`error` explaining what wasn't:

```json
{ "url": "https://example.com/this-page-does-not-exist-404", "finalUrl": "https://example.com/this-page-does-not-exist-404",
  "status": 404, "title": "Example Domain", "lang": "en", "favicon": "https://example.com/favicon.ico", "error": "HTTP 404" }
```

```json
{ "url": "https://www.apify.com/favicon.ico", "finalUrl": "https://apify.com/favicon.ico", "status": 200,
  "title": null, "og": {}, "twitter": {}, "jsonLdTypes": [], "favicon": null,
  "error": "Not HTML (content-type: image/x-icon)" }
```

Download the dataset as JSON, CSV, Excel, or query it over the API.

### Input

| Field | Type | Default | Notes |
|---|---|---|---|
| `urls` | string\[] | `["https://example.com", "https://apify.com"]`\* | Web page URLs to extract metadata from. |
| `maxConcurrency` | integer | `10` | How many URLs to fetch in parallel (1–50). |

\* The default only applies when `urls` is left empty — this is what a zero-config run uses, so it always
returns real metadata in a few seconds.

### Pricing

Pay per event, no subscription:

| Event | Price | When |
|---|---|---|
| Actor start | $0.001 | Once per run |
| URL processed | $0.001 | Once per URL attempted — including 404s, timeouts, and non-HTML responses, since fetching them still consumes platform resources (primary event) |

Worked examples:

- **10 URLs**: $0.001 + 10 × $0.001 = **$0.011**.
- **1,000 URLs**: $0.001 + 1,000 × $0.001 = **$1.001**.
- **The default sample run** (example.com, apify.com — 2 items): $0.001 + 2 × $0.001 = **$0.003**.
- **Free plan**: this Actor is HTTP-only, 256–512 MB, and a 1,000-URL run finishes in well under a minute —
  the $5/month free platform usage covers a large volume of occasional runs on top of the event price.

The minimum spend guard (`minimalMaxTotalChargeUsd`) is $0.05 — small enough for an agent to call this tool
with a tiny budget.

### Output fields

| Field | Description |
|---|---|
| `url` | Requested URL. |
| `finalUrl` | URL after redirects (`null` if the request failed outright). |
| `status` | HTTP status code (`0` = request never got a response). |
| `title` | `<title>`. |
| `description` | `<meta name="description">`. |
| `canonical` | `<link rel="canonical">`, resolved to an absolute URL. |
| `lang` | `<html lang="...">`. |
| `og` | Open Graph tags (`og:*`), keyed by the part after `og:`. |
| `twitter` | Twitter Card tags (`twitter:*`), keyed by the part after `twitter:`. |
| `jsonLdTypes` | `@type` value(s) from every `application/ld+json` block on the page. |
| `favicon` | Absolute favicon URL. Falls back to `/favicon.ico` when the page declares none (or an empty `data:` icon, which some sites use on purpose) — this is a best-effort guess, not verified to exist. |
| `error` | Set when the URL couldn't be fetched, returned a non-2xx status, or wasn't HTML. The item (and its charge) still exists — resources were spent either way — but most fields will be `null`. |

### Limitations & notes

- HTTP-only: pages that require JavaScript to render their metadata (rare — OpenGraph/Twitter Card/JSON-LD
  are meant to be readable from the raw HTML) won't get the client-rendered fields.
- Non-HTML responses (images, PDFs, etc.) are detected from the `Content-Type` header and skipped before
  parsing; you still get a dataset item with `status` and an `error`, and it is still charged.
- Public page metadata only — no login, no personal data.
- Not affiliated with Google, Meta, Twitter/X, or any site this Actor is pointed at; OpenGraph, Twitter Card,
  and JSON-LD are open, documented HTML conventions, not proprietary APIs.
- Found a bug or want a field added? Open an issue on this Actor's Issues tab.

# Actor input Schema

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

Web page URLs to extract metadata from (title, description, canonical, OpenGraph, Twitter Card, JSON-LD types, favicon, language). Leave empty to run the built-in sample (example.com, apify.com).

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

How many URLs to fetch in parallel.

## Actor input object example

```json
{
  "urls": [
    "https://example.com",
    "https://apify.com"
  ],
  "maxConcurrency": 10
}
```

# Actor output Schema

## `results` (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": [
        "https://example.com",
        "https://apify.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("alxlin/url-metadata").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": [
        "https://example.com",
        "https://apify.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("alxlin/url-metadata").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": [
    "https://example.com",
    "https://apify.com"
  ]
}' |
apify call alxlin/url-metadata --silent --output-dataset

```

## MCP server setup

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

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/GygfZH7UxgPLBrKqs/builds/IXzJxjgIZAX7WXrfI/openapi.json
