Documentation
Introduction
Link preview is a feature that provides users with a glimpse of a web page’s content before actually clicking on the link. It typically includes a preview of the page’s title, a brief description, an image, and the URL. Link previews are commonly used in messaging apps, social media platforms, and email clients to give users an idea of what to expect when they click on a shared link.
With the LinkPreview API, you can programmatically obtain detailed website information, including the title, preview image, and brief description, for any given URL.
This API documentation provides a comprehensive overview of how to use the API and the resources required for seamless integration.
How to get access
Authorization for LinkPreview relies on API keys, which clients must include in the X-Linkpreview-Api-Key
header for each request.
Sign up for your free API key here.
When developing a web application that runs in a browser, our suggestion is to construct a server-side application for managing API requests. This approach grants you the capability to oversee access, authentication, and rate-limiting within a secure server environment, safeguarding your API keys from public exposure.
Quick start
Simple GET Request
curl "https://api.linkpreview.net/?q=https://google.com" -H "X-Linkpreview-Api-Key: 123456"
Default JSON Response
{
"title":"Google",
"description":"Search webpages, images, videos and more.",
"image":"https://www.google.com/images/logo.png",
"url":"https://www.google.com"
}
Secure POST Request
You also have the option to make requests using the POST method. In the example below, we utilize the curl command line utility to transmit query parameters within the request body.
curl --data "q=https://google.com&fields=image_x,image_y,locale" https://api.linkpreview.net -H "X-Linkpreview-Api-Key: 123456"
API Endpoints
Endpoint | GET | POST | |
---|---|---|---|
https://api.linkpreview.net | Yes | Yes |
Query parameters
Basics
Our API uses query parameters since they are the simplest and the most common type of parameters. When using the GET request, they appear at the end of the request URL after the question mark (?), with name=value pairs separated by ampersands (&).
Name | Description | Example |
---|---|---|
q | URL to inspect* | https://www.google.com |
fields | Comma-separated list of fields to return | image_x, icon_type, locale |
key | Your API key** [deprecated] | 123456 |
* Since this parameter can contain reserved characters :/?#[]@!$&'()*+,;= it should be sent as percent-encoded (encodeURIComponent/urlencode).
** Deprecated, please use X-Linkpreview-Api-Key
header instead.
Default and Additional Fields
LinkPreview can return additional fields based on your current subscription plan. Example request - asking for three additional fields:
https://api.linkpreview.net/?fields=image_x,icon_type,locale&q=https://www.google.com
Name | Type | Description | Example Response | Subscription Plan |
---|---|---|---|---|
title | string | Website title. Returned by default. | Available on all plans | |
description | string | Description summary. Returned by default. | Search webpages, images, videos and more. | Available on all plans |
image | string | Preview image URL. Returned by default. | https://google.com/logo.png | Available on all plans |
url | string | Destination URL. Returned by default. | https://www.google.com | Available on all plans |
canonical | string | Canonical or “preferred” version of a web page. Defaults to blank "" if not found. | https://www.google.com/en | Basic, Pro, Enterprise |
locale | string | Web page’s locale formatted as language_TERRITORY. Defaults to “en_US” if not found. You can use this to detect RTL languages or to display other language-specific elements. | en_GB | Basic, Pro, Enterprise |
site_name | string | Short name that is used to represent the site. Defaults to top_level_domain+1. | google.com | Basic, Pro, Enterprise |
image_x | number | Image width in pixels. See image processing. | 600 | Pro, Enterprise |
image_y | number | Image height in pixels. See image processing. | 400 | Pro, Enterprise |
image_size | number | Image size in bytes. See image processing. | 643252 | Pro, Enterprise |
image_type | string | Image MIME content type. | image/jpeg | Pro, Enterprise |
icon | string | Website icon URL, also known as favicon. | https://google.com/fav.ico | Pro, Enterprise |
icon_x | number | Website icon width in pixels. | 50 | Pro, Enterprise |
icon_y | number | Website icon height in pixels. | 50 | Pro, Enterprise |
icon_size | number | Website icon size in bytes. | 48322 | Pro, Enterprise |
icon_type | string | Website icon MIME content type. | image/x-icon | Pro, Enterprise |
Unless otherwise noted, the default value for string type is an empty string ""
, and for a numeric type is zero 0
.
Default values are used when the parser is unable to find or extract the correct value from the requested URL.
Responses
Successful API response will be sent with HTTP status code 200 and it will contain application/json data. You should always validate/sanitize API response before using it.
Default Response
Successful HTTP response will be sent as 200 OK
and it will contain JSON data:
{
"title":"Google",
"description":"Search webpages, images, videos and more.",
"image":"https://www.google.com/images/logo.png",
"url":"https://www.google.com"
}
Extended Response
Extended API response will contain all requested additional fields. Make sure your current plan supports this:
{
"title":"Google",
"description":"Search webpages, images, videos and more.",
"image":"https://www.google.com/images/logo.png",
"url":"https://www.google.com"
"image_size": 896933,
"image_type: "image/gif",
"image_x: 1100,
"image_y: 440,
"icon": "https://www.google.com/favicon.ico",
"icon_type": "image/x-icon",
"icon_x": 32,
"icon_y": 32,
"locale: "en_US"
}
Errors
If a request cannot be completed successfully, the response from the API will contain HTTP status code 400 or higher. The response from the API may also contain one or more error elements in the response. This information can be used to determine what went wrong. Each of these elements may contain an error code, message, and (if applicable) other informative values to assist in debugging the problem.
Error Code | Description |
---|---|
400 | Generic error |
401 | Cannot verify API access key |
403 | Invalid or blank API access key |
423 | Forbidden by robots.txt - the requested website does not allow us to access this page |
425 | Invalid response status code (with the actual response code we got from the remote server) |
426 | Too many requests per second on a single domain |
429 | Too many requests / rate limit exceeded |
Example Error Response (HTTP Status code 401):
{
"title":"",
"description":"Linkpreview service denied",
"image":"",
"url":"",
"error":401
}
Rate limits
The counters for your API key reset on a rolling basis using the sliding window algorithm. Every hit to the API counts as one request.
Example for PRO Plan: If you made 500 requests at 10:15AM and 500 requests at 10:25AM, your API key would become temporarily blocked. This temporary block of your API key would cease at 11:15AM, at which point you could make 500 requests. At 11:25AM, you could then make another 500 requests.
The LinkPreview API incorporates several additional measures to handle sudden spikes in incoming traffic and enhance its overall stability.
Users who send many requests in quick succession may receive error responses, indicated by status code 503
, or could be temporarily banned by our upstream provider.
Parser
Our engine can only retrieve and parse publicly accessible pages and domains that use our custom integrations. Our crawler will respect robots.txt specification and some public pages may still be blocked due to this. The crawler identifies itself as “linkpreview” in the HTTP “User-agent” header field. Unfortunately, we cannot guarantee the correct response data for every single URL but we’re constantly working to resolve or minimize these edge-cases.
Some of the reasons for the failed, blank, or incomplete API response are beyond our control:
- Private pages or pages that require authentication or login
- Advanced bot-prevention installed at the targeted website
- Captcha protection
- Paywall protection
- Temporary networking issues or downtimes
- Webmasters not following best practices (no meta tags, open graph, or other content clues)
- Rendering page or adding meta tags with Javascript after document is loaded
- Restrictions based on IP address or range of addresses (known cloud providers block)
- Deep linking or platform specific links
- Crawling exclusion by robots.txt rules (error 423)
You can use our Preview Tool to try and report incorrect URL, or use Similar Validator to double-check if the URL can be parsed correctly by a different engine.
Image Processing and Validation
Sometimes the website can provide og:image that is not accessible, served via the insecure HTTP protocol, invalid, or the URL is simply wrong. LinkPreview API can help and process images to validate them and extract additional data such as image width, image height, content-type, and size. Supported web image formats include jpeg
, png
, gif
, ico
, and webp
images up to 5MB in size.
To validate the image, send your request with additional field image_size
and check if the returned image size is greater than zero. You can also use image width and height parameters to calculate the image orientation (portrait/landscape) and use that to decide which of your layouts to render. You can use image size to discard images that are too small or too big for your specific layout.
It is highly recommended to set up a proxy and serve all the images through your own cached and secure environment without leaking users IP addresses. See example.
Same-origin policy
Bypassing same-origin policy is handled automatically. You can use either CORS or JSONP requests to bypass same-origin policy in your front-end application. Since CORS allows you to use POST requests we recommend this method for all modern applications. JSONP is provided for compatibility reasons.
Caching
The LinkPreview API will cache requested pages and it will return the cached response for a while. Any page updates will get noted on its next crawl and not immediately. The exact TTL depends on various parameters and it can take up to a day for this cache to expire.
Per Domain Limits
Each request requires some resources to be allocated from the requested website. Too many connections to a single website imply a lot of burden for that server and can be flagged as a DoS attack. A Denial of Service(DoS) attack means that you are trying to make the server so busy that it’s incapable of dealing with other requests.
To avoid congestion and protect smaller websites, our service will rate-limit requests made to the same domain in short bursts.
You can make a maximum of 1 request each second to a single domain. If you go over this limit, the error 426 will be thrown.
However, this limit is not imposed on high-throughput domains such as Youtube, Amazon, Twitter, etc since they have a lot of resources to handle our requests.
If you for some reason need a higher limit, please contact us to request the increase.
Examples
Using the LinkPreview API requires some technical knowledge and programming skills. Please note that we do not provide support for your applications, plugins, cms, or web frameworks that make use of our API.
Codepen
Simple working example - Frontend with Bulma CSS
LinkPreview HTML Generator - Email Template
Javascript
POST request using javascript / Fetch API
var data = {q: 'https://www.google.com'}
fetch('https://api.linkpreview.net', {
method: 'POST',
headers: {
'X-Linkpreview-Api-Key': '123456',
},
mode: 'cors',
body: JSON.stringify(data),
}).then(res => {
if (res.status != 200) {
console.log(res.status)
throw new Error('something went wrong');
}
return res.json()
}).then(response => {
console.log(response)
}).catch(error => {
console.log(error)
})
POST request using javascript / axios
import axios from 'axios'
axios.post('https://api.linkpreview.net', {
q: 'https://www.google.com',
},
{
headers: {
'X-Linkpreview-Api-Key': '123456'
}
}).then(resp => {
console.log(resp.data)
}).catch(err => {
// something went wrong
console.log(err.response.status)
})
PHP, Python, Ruby, cURL
Shell / cURL
curl --data "q=https://www.google.com" https://api.linkpreview.net -H "X-Linkpreview-Api-Key: 123456"
PHP using curl
$target = urlencode("https://www.google.com");
$key = "123456";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "https://api.linkpreview.net?&q={$target}");
$headers = [
"X-Linkpreview-Api-Key: {$key}"
];
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$output = json_decode(curl_exec($ch));
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status != 200) {
// something went wrong
print_r($status);
die;
}
print_r($output);
POST request using Python3
import requests
api_url = 'https://api.linkpreview.net'
api_key = '123456'
target = 'https://www.google.com'
response = requests.get(
api_url,
headers={'X-Linkpreview-Api-Key': api_key},
params={'q': target},
)
print(response.json())
POST request using Ruby
require('httparty')
query = {
"q" => "https://www.google.com",
}
headers = {
"X-Linkpreview-Api-Key" => "123456",
}
response = HTTParty.post(
"https://api.linkpreview.net",
:query => query,
:headers => headers
)
puts response.body
JQuery
jQuery simple CORS request
$.ajax({
url: "https://api.linkpreview.net?q=https://www.google.com",
headers: {
'X-Linkpreview-Api-Key':'123456',
},
success: function(result) {
console.log(result);
},
error: function(error) {
// something went wrong
console.log(error.status)
}
});
jQuery preflight CORS request
$.ajax({
url: "https://api.linkpreview.net?q=https://www.google.com",
headers: {
'X-Linkpreview-Api-Key':'123456',
},
type: "GET",
contentType: "application/json",
success: function(result){
console.log(result);
},
error: function(error) {
// something went wrong
console.log(error.status)
}
});
Full Frontend Example
Frontend code using Bulma CSS and Javascript with Fetch API:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@0.9.1/css/bulma.min.css">
</head>
<body>
<section class="section">
<div class="container">
<div class="box" style="width:300px">
<img id="myimage" src="">
<div class="is-clipped">
<div id="mytitle" class="has-text-weight-bold"></div>
<div id="mydescription" class="mt-2"></div>
<div id="myurl" class="mt-2 is-size-7"></div>
</div>
</div>
</div>
</section>
<script>
var data = {q: 'https://www.google.com'}
var key = "123456"
fetch('https://api.linkpreview.net', {
method: 'POST',
headers: {
'X-Linkpreview-Api-Key': key,
},
mode: 'cors',
body: JSON.stringify(data),
})
.then(res => res.json())
.then(response => {
document.getElementById("mytitle").innerHTML = response.title
document.getElementById("mydescription").innerHTML = response.description
document.getElementById("myimage").src = response.image
document.getElementById("myurl").innerHTML = response.url
})
</script>
</body>
</html>