{"openapi":"3.0.0","info":{"title":"LinkMetadata API","version":"1.0.0","description":"Free API for extracting link-preview metadata from public web pages.\n\nThe API returns normalized title, description, image, favicon, canonical URL,\nraw Open Graph tags, raw Twitter Card tags, and readable safety tags. It also\nincludes an image proxy endpoint for preview images that need permissive CORS\nheaders. No authentication, signup, or API key is required.\n\nRate limit: 20 requests per 10 seconds per IP address. When the limit is\nreached, that IP is blocked for 10 seconds before retrying.","contact":{"name":"Faycal Arbai","email":"arbai.faycal@icloud.com"},"license":{"name":"MIT","url":"https://opensource.org/license/mit/"}},"servers":[{"url":"https://api.linkmetadata.com","description":"Production API"}],"tags":[{"name":"Metadata","description":"Extract normalized link-preview metadata and safety tags from a URL."},{"name":"Images","description":"Proxy supported preview images with browser-friendly CORS headers."},{"name":"Component","description":"Load the browser web component used to render link previews."}],"externalDocs":{"description":"LinkMetadata documentation","url":"https://linkmetadata.com/docs"},"components":{"schemas":{"ImageMetadata":{"type":"object","properties":{"url":{"type":["string","null"],"format":"uri","default":null,"description":"Absolute URL of the image. Null if no suitable image is found.","example":"https://example.com/card.png"},"type":{"type":["string","null"],"default":null,"description":"MIME type of the image (e.g., 'image/png'). Derived primarily from 'og:image:type'.","example":"image/png"},"width":{"type":["number","null"],"default":null,"description":"Width of the image in pixels. Derived primarily from 'og:image:width'.","example":1200},"height":{"type":["number","null"],"default":null,"description":"Height of the image in pixels. Derived primarily from 'og:image:height'.","example":630},"size":{"type":["number","null"],"default":null,"description":"Size of the image in bytes. Derived from the 'og:image:size' tag if available, otherwise null."},"cors_safe":{"type":"boolean","description":"Optional flag indicating if the image URL is CORS-safe. If true, browser JavaScript can fetch it cross-origin. If false or undefined, direct browser access may be blocked."}},"description":"Structured metadata for the primary image associated with the page. URL is resolved to absolute.","example":{"url":"https://example.com/card.png","type":"image/png","width":1200,"height":630,"size":184523,"cors_safe":true}},"SafetyTag":{"type":"string","enum":["adult","security_threat","suspicious_domain"],"description":"Readable URL safety signal.","example":"adult"},"Metadata":{"type":"object","properties":{"title":{"type":["string","null"],"default":null,"description":"The primary title for the page. Prioritizes preferred source (OG/Twitter), then falls back to the HTML <title> tag. Null if none found.","example":"Example article"},"description":{"type":["string","null"],"default":null,"description":"The primary description for the page. Prioritizes preferred source (OG/Twitter), then falls back to the HTML <meta name=\"description\"> tag. Null if none found.","example":"A short summary suitable for a link preview."},"url":{"type":"string","format":"uri","description":"The canonical or representative URL for the page. Prioritizes preferred source (OG/Twitter), then <link rel=\"canonical\">, then the originally fetched URL. Always an absolute URL.","example":"https://example.com/article"},"image":{"$ref":"#/components/schemas/ImageMetadata"},"type":{"type":["string","null"],"default":null,"description":"The type of the primary object on the page (e.g., 'website', 'article', 'summary_large_image'). Derived from 'og:type' or 'twitter:card' based on preference.","example":"article"},"favicon":{"allOf":[{"$ref":"#/components/schemas/ImageMetadata"},{"description":"Structured metadata for the page's favicon. Prioritizes apple-touch-icon, mask-icon, SVG icons, then other <link rel=\"icon\"> tags, and finally falls back to '/favicon.ico'. Null if none found."}]},"og":{"type":"object","additionalProperties":{"type":"string"},"description":"A key-value map of all Open Graph (og:*) meta tags found on the page. Values are the raw 'content' attributes.","example":{"og:title":"Example article","og:description":"A short summary suitable for a link preview.","og:image":"https://example.com/card.png"}},"twitter":{"type":"object","additionalProperties":{"type":"string"},"description":"A key-value map of all Twitter Card (twitter:*) meta tags found on the page. Values are the raw 'content' attributes.","example":{"twitter:card":"summary_large_image","twitter:title":"Example article"}},"safety_tags":{"type":"array","items":{"$ref":"#/components/schemas/SafetyTag"},"default":[],"description":"Human-readable safety tags associated with the URL. Possible values: adult, security_threat, suspicious_domain. Empty when no tag applies or safety lookup fails.","example":["adult","security_threat"]}},"required":["url","image","favicon","og","twitter"],"example":{"title":"Example article","description":"A short summary suitable for a link preview.","url":"https://example.com/article","image":{"url":"https://example.com/card.png","type":"image/png","width":1200,"height":630,"size":184523,"cors_safe":true},"type":"article","favicon":{"url":"https://example.com/favicon.ico","type":"image/x-icon","width":null,"height":null,"size":null,"cors_safe":true},"og":{"og:title":"Example article","og:description":"A short summary suitable for a link preview.","og:image":"https://example.com/card.png"},"twitter":{"twitter:card":"summary_large_image","twitter:title":"Example article"},"safety_tags":["adult","security_threat"]}},"Error":{"type":"object","properties":{"code":{"type":"integer","description":"HTTP status code associated with the error.","example":422},"message":{"type":"string","description":"Human-readable error message.","example":"Failed to fetch https://example.com: fetch failed"}},"required":["code","message"]},"ProxyError":{"type":"object","properties":{"error":{"type":"string","description":"Human-readable proxy error message.","example":"URL does not point to an image"}},"required":["error"]}},"parameters":{}},"paths":{"/v1/metadata":{"get":{"operationId":"getMetadata","summary":"Extract metadata for a URL","description":"Fetches a public URL and returns normalized link-preview metadata.\n\n\t\tThe response includes normalized fields for title, description, canonical URL, image,\n\t\ttype, favicon, raw Open Graph tags, raw Twitter Card tags, and readable safety tags.\n\t\tURL fields are resolved to absolute URLs where possible.\n\n\t\tThe API is free to use and does not require authentication, signup, or API keys.\n\t\tRequests are rate limited to 20 requests per 10 seconds per IP address. When the\n\t\tlimit is reached, that IP is blocked for 10 seconds before retrying.","tags":["Metadata"],"externalDocs":{"description":"Metadata response guide","url":"https://linkmetadata.com/docs/reference/metadata-response"},"parameters":[{"schema":{"type":"string","format":"uri","description":"Absolute HTTP or HTTPS URL of the page to inspect.","example":"https://example.com/article"},"required":true,"description":"Absolute HTTP or HTTPS URL of the page to inspect.","name":"url","in":"query"},{"schema":{"type":"string","enum":["og","twitter"],"default":"og","description":"Preferred source for normalized title, description, url, image, and type fields.\n\t\t\t\t\t\tUse \"og\" to prefer Open Graph tags before Twitter Card tags.\n\t\t\t\t\t\tUse \"twitter\" to prefer Twitter Card tags before Open Graph tags.\n\t\t\t\t\t\tHTML title, meta description, canonical link, and favicon values remain fallback sources.","example":"og"},"required":false,"description":"Preferred source for normalized title, description, url, image, and type fields.\n\t\t\t\t\t\tUse \"og\" to prefer Open Graph tags before Twitter Card tags.\n\t\t\t\t\t\tUse \"twitter\" to prefer Twitter Card tags before Open Graph tags.\n\t\t\t\t\t\tHTML title, meta description, canonical link, and favicon values remain fallback sources.","name":"prefer","in":"query"},{"schema":{"type":"boolean","default":true,"description":"Reserved for fallback control. Current responses use normal fallback behavior regardless of this value.","example":true},"required":false,"description":"Reserved for fallback control. Current responses use normal fallback behavior regardless of this value.","name":"allow_fallback","in":"query"}],"responses":{"200":{"description":"Metadata extracted successfully.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Metadata"},"example":{"title":"Example article","description":"A short summary suitable for a link preview.","url":"https://example.com/article","image":{"url":"https://example.com/card.png","type":"image/png","width":1200,"height":630,"size":184523,"cors_safe":true},"type":"article","favicon":{"url":"https://example.com/favicon.ico","type":"image/x-icon","width":null,"height":null,"size":null,"cors_safe":true},"og":{"og:title":"Example article","og:image":"https://example.com/card.png"},"twitter":{"twitter:card":"summary_large_image"},"safety_tags":["adult","security_threat"]}}}},"302":{"description":"Redirecting to a default showcase URL because the 'url' parameter was missing. The 'Location' header will contain the redirect target.","headers":{"Location":{"schema":{"type":"string","format":"uri","description":"The URL to redirect to, including the default 'url' query parameter."},"required":true,"description":"The URL to redirect to, including the default 'url' query parameter."}}},"400":{"description":"Input Validation Error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"number","example":7001},"message":{"type":"string","example":"Input Validation Error"},"path":{"type":"array","items":{"type":"string","example":"body"}}},"required":["code","message","path"]}}},"required":["success","errors"]}}}},"422":{"description":"The URL was valid, but the target page could not be fetched or processed successfully.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"message":{"type":"string","example":"Failed to fetch or parse HTML for the given URL."}}}]}}}},"429":{"description":"The per-IP rate limit was reached. Wait 10 seconds before retrying."},"500":{"description":"An unexpected server error occurred while processing the request.","content":{"application/json":{"schema":{"allOf":[{"$ref":"#/components/schemas/Error"},{"type":"object","properties":{"message":{"type":"string","example":"An internal server error occurred."}}}]}}}}}}},"/v1/cors-proxy":{"get":{"operationId":"proxyImageForCors","summary":"Proxy a preview image with CORS headers","description":"Fetches an HTTP or HTTPS image URL, verifies that the upstream response is an image,\n\t\tand streams it back with permissive CORS headers. This is intended for preview images\n\t\treturned by the metadata endpoint when direct browser access is blocked by CORS.\n\n\t\tOnly JPEG, PNG, GIF, and WebP image bytes are accepted. The endpoint is free to use\n\t\tand shares the public per-IP rate limit: 20 requests per 10 seconds, followed by a\n\t\t10-second block when the limit is reached.","tags":["Images"],"externalDocs":{"description":"Images and CORS guide","url":"https://linkmetadata.com/docs/explanation/images-and-cors"},"parameters":[{"name":"url","in":"query","required":true,"description":"Absolute HTTP or HTTPS image URL to proxy. Missing, invalid, and non-image URLs return JSON errors.","schema":{"type":"string","format":"uri"},"example":"https://example.com/card.png"}],"responses":{"200":{"description":"Image proxied successfully.","headers":{"Access-Control-Allow-Origin":{"schema":{"type":"string","description":"Always '*'."},"required":true,"description":"Always '*'."},"Cache-Control":{"schema":{"type":"string","description":"Successful proxy responses are cacheable for one day."},"required":true,"description":"Successful proxy responses are cacheable for one day."},"Content-Type":{"schema":{"type":"string","description":"Original upstream image content type."},"required":true,"description":"Original upstream image content type."}},"content":{"image/png":{"schema":{"type":"string","description":"Binary image response body.","format":"binary"}},"image/jpeg":{"schema":{"type":"string","description":"Binary image response body.","format":"binary"}},"image/gif":{"schema":{"type":"string","description":"Binary image response body.","format":"binary"}},"image/webp":{"schema":{"type":"string","description":"Binary image response body.","format":"binary"}}}},"400":{"description":"The URL was missing, invalid, not HTTP/S, not an image, empty, or not a supported image format.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyError"}}}},"429":{"description":"The per-IP rate limit was reached. Wait 10 seconds before retrying."},"default":{"description":"The upstream image request failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ProxyError"}}}}}}},"/component.js":{"get":{"operationId":"getPreviewComponent","summary":"Load the link preview web component","description":"Returns the browser custom element bundle for <link-metadata-preview>.\n\t\tInclude this script on a page, then render the component with a data-url attribute\n\t\tor pre-fetched metadata in data-meta.","tags":["Component"],"externalDocs":{"description":"Component reference","url":"https://linkmetadata.com/docs/reference/component"},"responses":{"200":{"description":"JavaScript module containing the preview web component.","content":{"application/javascript":{"schema":{"type":"string","description":"JavaScript source for the web component.","example":"customElements.define('link-metadata-preview', LinkMetadataPreview);"}}}}}}}},"webhooks":{}}