# Twitter Card Metadata Validator (`automation-lab/twitter-card-metadata-validator`) Actor

Validate Twitter Card tags on public URLs, resolve Open Graph fallbacks, and export actionable missing, invalid, duplicate, and truncation diagnostics.

- **URL**: https://apify.com/automation-lab/twitter-card-metadata-validator.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.44 / 1,000 page extracteds

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

## Twitter Card Metadata Validator

Validate Twitter Card metadata across public web pages and export deterministic diagnostics for website QA. This **Twitter Card validator** fetches each supplied URL, extracts `twitter:*` and Open Graph tags, resolves the values X can use, and explains missing, invalid, duplicate, or likely truncated fields.

It returns one structured row per URL, including fetch failures. Use the rows in scheduled Tasks, CI checks, spreadsheets, dashboards, or regression comparisons without manually opening a browser validator for every release page.

### What does Twitter Card Metadata Validator do?

For every unique input URL, the Actor:

1. fetches server-rendered public HTML;
2. follows up to five public redirects safely;
3. extracts all `twitter:*` and `og:*` meta values;
4. chooses explicit Twitter values before documented Open Graph fallbacks;
5. resolves relative image references against the final page URL;
6. validates card type, required fields, URLs, account handles, duplicates, and common length risks;
7. emits stable diagnostic codes and human-readable remediation messages.

This is metadata validation, not a screenshot of X's live composer. Rendering can change on X, while the Actor reports the page data available to a card consumer at check time.

### Who is it for?

- **Developers** checking social metadata before and after releases.
- **SEO teams** auditing templates across landing pages and articles.
- **Content teams** finding pages with missing social titles, descriptions, or images.
- **QA engineers** comparing stable error codes between deployments.
- **Agencies** exporting repeatable client-site audit evidence.
- **Platform teams** monitoring metadata regressions with scheduled Apify Tasks.

### Why use structured validation?

A visual preview alone is difficult to compare at scale. The dataset exposes both effective values and their source tags, so you can tell whether a title came from `twitter:title`, `og:title`, or the HTML `<title>`.

Unlike a generic meta-tag dump, each record includes:

- an overall `isValid` result;
- error and warning counts;
- stable machine-readable diagnostic codes;
- fallback source attribution;
- optional raw tags for exact regression diffs;
- a free diagnostic row when a site cannot be fetched.

### Supported Twitter Card checks

The Actor recognizes `summary`, `summary_large_image`, `player`, and `app` card types. It reports an error for unknown types.

When `twitter:card` is absent but Open Graph card fields exist, the Actor infers `summary` and emits `CARD_TYPE_INFERRED` as a warning. It checks title availability, description and image presence, invalid image URLs, repeated or empty tags, likely title or description truncation, malformed `twitter:site` and `twitter:creator` handles, and required player-card fields.

A warning highlights a quality or portability risk. An error means the computed card fails a core validation rule. `isValid` is true only when there are no error-severity diagnostics.

### Extracted Twitter Card data

| Field | Meaning |
| --- | --- |
| `requestedUrl` | Original input URL |
| `finalUrl` | URL after redirects |
| `fetched` | Whether HTML was fetched and parsed |
| `statusCode` | Final HTTP status |
| `cardType` | Explicit or inferred card type |
| `cardTypeSource` | `twitter` or `inferred` |
| `isValid` | Whether no error diagnostic exists |
| `errorCount` / `warningCount` | Fast audit totals |
| `effective` | Resolved title, description, image, alt text, site, and creator |
| `valueSources` | Exact tag selected for each effective value |
| `diagnostics` | Stable code, severity, field, and message objects |
| `rawTags` | All Twitter/Open Graph values when enabled |
| `checkedAt` | UTC validation timestamp |

### How to validate Twitter Card metadata

1. Open the Actor in Apify Console.
2. Add one or more public page URLs under **Web pages to validate**.
3. Keep raw tags enabled when you need exact before/after comparisons.
4. Set a page limit and conservative concurrency for the target sites.
5. Click **Start**.
6. Inspect the default dataset's overview table.
7. Filter `isValid = false`, or export `diagnostics` to your QA pipeline.

The default input audits real Apify SDK project pages and produces useful output immediately.

### Input parameters

| Input | Type | Default | Notes |
| --- | --- | --- | --- |
| `startUrls` | URL array | required | Public HTTP(S) pages; duplicates run once |
| `maxItems` | integer | `20` | Maximum unique pages, from 1 to 1,000 |
| `maxConcurrency` | integer | `5` | Parallel fetches, from 1 to 20 |
| `requestTimeoutSecs` | integer | `30` | Per-attempt timeout, from 5 to 120 seconds |
| `maxRetries` | integer | `2` | Transient retries, from 0 to 3 |
| `includeRawTags` | boolean | `true` | Include all discovered Twitter and Open Graph tags |

Localhost, private-network addresses, credentials embedded in URLs, and non-HTTP protocols are blocked. Each redirect destination is checked again.

### Output example

A current run against `https://apify.com` produces the following shape (long raw-tag values shortened here):

```json
{
  "requestedUrl": "https://apify.com",
  "finalUrl": "https://apify.com/",
  "fetched": true,
  "statusCode": 200,
  "contentType": "text/html",
  "cardType": "summary_large_image",
  "cardTypeSource": "twitter",
  "isValid": true,
  "errorCount": 0,
  "warningCount": 0,
  "effective": {
    "title": "Apify: The largest marketplace of trusted tools for AI",
    "description": "Discover and use cloud tools for web data and automation.",
    "image": "https://apify.com/og-image.png",
    "imageAlt": null,
    "site": "@apify",
    "creator": null
  },
  "valueSources": {
    "title": "twitter:title",
    "description": "twitter:description",
    "image": "twitter:image",
    "imageAlt": null,
    "site": "twitter:site",
    "creator": null
  },
  "diagnostics": [],
  "checkedAt": "2026-09-06T12:00:00.000Z"
}
```

Exact content belongs to the target page and can change between runs. Use the dataset schema rather than hard-coding sample text.

### How much does it cost to validate Twitter Card metadata?

Pay-per-event pricing combines a **$0.001 start fee** with one page event for each successfully fetched page. Failed fetches still produce a diagnostic row but have no page event charge.

Current page-event tiers are:

| Apify plan | Price per fetched page |
| --- | ---: |
| FREE | $0.00276 |
| BRONZE | $0.0024 |
| SILVER | $0.001872 |
| GOLD | $0.00144 |
| PLATINUM | $0.00144 |
| DIAMOND | $0.00144 |

At BRONZE, 10 successfully fetched pages cost about **$0.025** including the start fee, 100 cost about **$0.241**, and 1,000 cost about **$2.401**. Actual total depends on how many input pages fetch successfully. Retries do not create extra page charges.

### Schedule metadata regression monitoring

Create an Apify Task with the pages in one release surface, then schedule it after deployments or once per day. Keep `includeRawTags` enabled when exact tag-level diffs matter, or disable it for smaller audit records.

Useful automation patterns include:

- alert when a row changes from valid to invalid;
- compare `diagnostics[].code` against the previous dataset;
- require `effective.image` on campaign landing pages;
- track which templates rely on Open Graph fallback;
- export failed URLs to a developer backlog.

The Actor does not send alerts or retain a historical diff itself. Connect scheduled run output to your preferred automation or storage system.

### Export and integration options

The default dataset works with JSON, CSV, Excel, XML, RSS, and API clients supported by Apify. Nested objects are most expressive in JSON.

You can connect runs to:

- Google Sheets for editorial review;
- webhooks for CI or incident automation;
- Make or Zapier for notifications;
- a warehouse for longitudinal audits;
- another Actor for remediation triage.

Each input URL has a result row even when fetching fails, which makes batch accounting deterministic.

### Run through the Apify API with cURL

Replace `YOUR_TOKEN` with an Apify API token:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~twitter-card-metadata-validator/runs?token=YOUR_TOKEN&waitForFinish=120" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [
      {"url": "https://apify.com"},
      {"url": "https://github.com/apify/apify-sdk-js"}
    ],
    "maxItems": 2,
    "includeRawTags": true
  }'
```

Read the run's `defaultDatasetId`, then fetch its items through the dataset API.

### Run with JavaScript

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/twitter-card-metadata-validator').call({
  startUrls: [{ url: 'https://apify.com' }],
  maxItems: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0].isValid, items[0].diagnostics);
```

### Run with Python

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/twitter-card-metadata-validator').call(run_input={
    'startUrls': [{'url': 'https://apify.com'}],
    'maxItems': 1,
})
items = client.dataset(run['defaultDatasetId']).list_items().items
print(items[0]['isValid'], items[0]['diagnostics'])
```

### Use with MCP and AI agents

#### Claude Code setup

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/twitter-card-metadata-validator"
```

#### Claude Desktop, Cursor, and VS Code setup

Claude Desktop, Cursor, and VS Code can use the equivalent HTTP MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/twitter-card-metadata-validator"
    }
  }
}
```

Example prompts:

- “Validate the Twitter Card metadata on these five release URLs and summarize errors by code.”
- “Run the validator for our landing pages and list pages relying on Open Graph fallback.”
- “Compare these result rows with yesterday's export and flag new missing images.”

### Limits and failure behavior

The Actor reads server-rendered HTML and does not execute client-side JavaScript. Metadata injected only after browser rendering will appear missing. Login-protected pages, robots or firewall restrictions, rate limits, and bot challenges may prevent a fetch.

A page-level failure becomes a typed `FETCH_FAILED` dataset row and is not charged as a page event. Other pages continue. An invalid or empty top-level input fails the run. Responses above 5 MB, non-HTML responses, private destinations, and more than five redirects are rejected for safety.

Retries apply only to network failures, rate limits, and temporary server errors. Increasing retries can improve resilience but does not bypass authentication or stable access denial.

### Legality and responsible use

Only submit URLs you are authorized to access and audit. Respect site terms, robots policies, rate limits, copyright, privacy obligations, and applicable law. The Actor accesses anonymously public metadata and does not bypass logins or collect private account data.

Metadata validation is technical assistance, not a guarantee that X will display a card. Social platforms can cache prior metadata, apply their own policies, crop images, or suppress previews.

### Troubleshooting and FAQ

#### Why does a page return `FETCH_FAILED`?

Check the diagnostic message. The page may reject automated HTTP clients, require login, return non-HTML content, time out, or redirect to a private/non-public host. Test the final public URL and lower concurrency for fragile sites.

#### Why is `twitter:card` missing but `cardType` is `summary`?

Open Graph title, description, or image tags allow a summary-card fallback. The Actor records `cardTypeSource: "inferred"` and emits `CARD_TYPE_INFERRED` so you can choose whether to add an explicit tag.

#### Does the Actor verify the image can be downloaded?

No. It validates and resolves the image URL syntax but does not download media. This keeps runs fast and avoids unexpected transfer costs.

#### Does it show the exact visual Twitter Card preview?

No. It computes effective metadata fields and diagnostics. X controls final rendering, caching, cropping, and policy decisions.

#### Can I compare runs automatically?

Yes. Schedule a Task and compare `isValid`, counts, effective fields, diagnostic codes, or raw tags in your integration. Historical diffing is intentionally left to your workflow.

#### Are redirects supported?

Yes, up to five. Every redirect target must resolve exclusively to public IP addresses.

### Related automation-lab Actors

For broader page quality work, use [Multi-page On-page SEO Audit Crawler](https://apify.com/automation-lab/multi-page-on-page-seo-audit) to crawl bounded websites and export titles, descriptions, headings, canonical tags, indexability, links, and deterministic SEO issues.

Use this Actor when the buyer job is specifically Twitter Card and Open Graph fallback validation for supplied URLs; use the broader crawler when page discovery and general on-page SEO signals are required.

### Versioning

The dataset contract uses stable camelCase fields and machine-readable diagnostic codes. New optional checks may add diagnostic codes or nullable fields in later releases. Pin Actor versions in strict integrations and review the changelog before upgrading.

# Actor input Schema

## `startUrls` (type: `array`):

Public HTTP(S) page URLs. Duplicate URLs are processed once; local and private-network destinations are rejected.

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

Maximum number of unique input URLs to process.

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

Number of pages fetched in parallel. Reduce this for fragile sites.

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

Maximum seconds allowed for each fetch attempt.

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

Retries for timeouts, network errors, rate limits, and temporary server errors.

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

Include every discovered twitter:\* and og:\* value in each result for regression comparisons.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://github.com/apify/apify-sdk-js"
    },
    {
      "url": "https://docs.apify.com/sdk/js/"
    }
  ],
  "maxItems": 20,
  "maxConcurrency": 5,
  "requestTimeoutSecs": 30,
  "maxRetries": 2,
  "includeRawTags": true
}
```

# Actor output Schema

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

Typed Twitter Card metadata, effective fields, and diagnostics.

# 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 = {
    "startUrls": [
        {
            "url": "https://github.com/apify/apify-sdk-js"
        },
        {
            "url": "https://docs.apify.com/sdk/js/"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/twitter-card-metadata-validator").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 = { "startUrls": [
        { "url": "https://github.com/apify/apify-sdk-js" },
        { "url": "https://docs.apify.com/sdk/js/" },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/twitter-card-metadata-validator").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 '{
  "startUrls": [
    {
      "url": "https://github.com/apify/apify-sdk-js"
    },
    {
      "url": "https://docs.apify.com/sdk/js/"
    }
  ]
}' |
apify call automation-lab/twitter-card-metadata-validator --silent --output-dataset

```

## MCP server setup

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

```

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/nYE5QGe0fz522S5gP/builds/vpCgYpJhqEMEKhhAu/openapi.json
