1. General

Note: the docs contain some opinions. You can treat them as "best practices" for working with embeds if you'd like. Of course, they are still just our opinions.

Different types of embeds

On the highest level there are two types of embeds: oEmbed and "regular embed". What they are and how they differ is this: oEmbed is (or should be) specifically crafted by the website owners to look optimal for that website; good examples are Twitter and Facebook. In reality, oEmbeds come in all sizes and shapes and often may be far from optimal.

Regular embeds, however, will look uniform across the field. Note, that "regular embed" is not an official term. Instead, they cover everything else which is not an oEmbed. These comprise of The Open Graph protocol and web scraping techniques. Regular embeds are divided further into different content types; more on that in a bit.

Because of the issues with oEmbeds and the uniform characteristics of other regular embeds we recommend to avoid using oEmbeds. Our example code on the bottom of this page will have an implementation which reflects this. Note also that our API will return both oEmbed and regular embed in the response, whenever available. The regular embed should be available for most of the time, while oEmbed may or may not be.

Security

oEmbeds have other problems as well — namely, the security isn't great. As it stands, oEmbed can contain any markup and thus injecting that into your site isn't safe, unless you really can trust the site. The safest way to present an oEmbed is to serve it in an iframe. Some sites (Twitter, Facebook) can probably be trusted, and serving them directly without an iframe should be OK.

There are other security consideration as well and this one applies to regular embeds: Linking to 3rd party images directly opens up Cross-Site Request Forgery (CSRF) and other vulnerabilities, and it's advisable to host all the images (including favicons) through a your own domain. That domain can be called, for example, safe.yourdomain.com, and it should be setup as a reverse proxy with NGINX to host the images. We will provide a sample NGINX configuration for that.

2. API

2.1. Request

The API is simple; you will issue a GET request to our url, and will get a JSON document as a response. The API endpoint is as follows:

https://api.embed.rocks/api/?url=<url>

The API Key has to be provided in the headers as x-api-key.

Here are some examples of calling the API. We are requestin the url https://www.bbc.com/sport/rugby-union/44325008 which translates to https%3A%2F%2Fwww.bbc.com%2Fsport%2Frugby-union%2F44325008 after encoding. Don't forget to encode, otherwise you may get nasty surprises!

$ curl -H "x-api-key:mykey" https://api.embed.rocks/api?url=https%3A%2F%2Fwww.bbc.com%2Fsport%2Frugby-union%2F44325008

The supplied url should be urlencoded. mykey is your API key (you'll find it in Dashboard).

Request params

autoplay

You can add &autoplay=1 to the query, which will make most videos (only flash videos, since you can/should control html5 videos yourself) autoplayable.

include

You can get extra data my adding &include=property1,property2 to the query:

include=articleParses the article (using "Readability" type of login) from the html page
include=sourceThe whole html source code
include=oembedoEmbed data

skip

Some fields can be left out from the response to speed up the processing and data the transfer:

skip=htmlDon't add the helper "html" field.

maxwidth

You can add &maxwidth=<number> to the query, which will determine the maximum width of the oembed element.

2.2. Response

As a response, you will get a JSON document with relevant data fields. Depending on the type of webpage you will get a different types of responses, as explained below:

Common fields

There are a few fields that are common to all responses. These are:

NameValue
typerich | video | audio | photo
urlThe url you provided
oembedThe oembed data as such
htmlA helper field to get you going faster. Includes some of the other fields (title. description, image). You can suppress this (by adding skip=html to the url) if you don't need this.
errorIf there was an error, this will be set (see below)

Different types (rich, video, audio, photo) of responses have different fields, as show below:

type: rich

These are probably the most common types of responses. This indicates, that the page contains text + images (although it may contain only text too).

Note, the value can contain {...} which represents a JSON document. The value can also contain [...] which represents an array. An array can contain {...} (JSON documents).

Also note that the argument safe is for displaying the image safely and securely – we will provide sample NGINX configuration for this.

NameValue
typerich
urlThe url you provided
sitehostname of the site (www.example.com)
titleTitle of the page
descriptionA short (250 characters) snippet of text; best describing the page
authorAuthor's name (not always available)
published_dateNot always available, but if it is, it will be:

{
  • parsed: date in ISO 8601 format
  • orig: date in original format
}

Note that sometimes the site provides only date, not time, and the published_date.parsed date will show 00:00:00 for the time. in those cases you should be suspicious and check the original time as well (which is provided as published_date.orig).
articleThe whole article
sourceThe whole page source (with the parameter include=source)
favicon{
  • url: url of the favicon
  • safe: the same url as an urlencoded component
}
images[ {
  • url: url of the image
  • safe: the same url as an urlencoded component
  • width: width of the image in pixels
  • height: height of the image in pixels
} ]

type: video

NameValue
typevideo
urlThe url you provided
sitehostname of the site (www.example.com)
titleTitle of the page
descriptionA short (250 characters) snippet of text; best describing the page
favicon{
  • url: url of the favicon
  • safe: the same url as an urlencoded component
}
videos[ {
  • url: url of the video
  • safe: the same url as an urlencoded component
  • type: content-type of the video
} ]

Note that we return videos of content type text/html and application/flash if the site has told us that they are videos. Thus, we treat them as videos.

type: audio

NameValue
typeaudio
urlThe url you provided
sitehostname of the site (www.example.com)
titleTitle of the page
descriptionA short (250 characters) snippet of text; best describing the page
favicon{
  • url: url of the favicon
  • safe: the same url as an urlencoded component
}
audios[ {
  • url: url of the audio
  • safe: the same url as an urlencoded component
} ]

type: photo

NameValue
typephoto
urlThe url you provided
images[ {
  • url: url of the image
  • safe: the same url as an urlencoded component
} ]

type: error

NameValue
typeerror
errorError object
msgError message

2.3. Backup API

There is a backup API in case the primary API is down. The endpoint is:

https://api2.embed.rocks/api/

We suggest that you use this API for some specific sites, because the main API a 30 second timeout, and some sites may require more time. The sites that we know of are now just tiktok.com.

3. Examples

3.1 A rich type

The rich type is probably the most common type; it contains usually an image and some text. The URL http://edition.cnn.com/2017/01/05/aviation/safest-airlines-for-2017/index.html will get us the following JSON document:

{
	"article": "<div>(article omitted in the example)</div>",
	"description": "AirlineRatings.com declares 2016 one of the safest years in aviation history as it names what it says are the world's safest airlines.",
	"favicon": {
		"safe": "http%3A%2F%2Fedition.cnn.com%2Ffavicon.ie9.ico",
		"url": "http://edition.cnn.com/favicon.ie9.ico"
	},
	"images": [
		{
			"safe": "http%3A%2F%2Fi2.cdn.cnn.com%2Fcnnnext%2Fdam%2Fassets%2F160412104516-airplane-generic-ii-super-tease.jpg",
			"url": "http://i2.cdn.cnn.com/cnnnext/dam/assets/160412104516-airplane-generic-ii-super-tease.jpg"
		}
	],
	"site": "CNN",
	"title": "The world's safest airlines for 2017?",
	"type": "rich",
	"url": "http://edition.cnn.com/2017/01/05/aviation/safest-airlines-for-2017/index.html",
	"videos": []
}

It will look like this when rendered (you render this yourself, so this is just an example):

3.2 An URL from YouTube.com

Here we will see a type of video — the videos array will contain items. The videos array will be sorted roughly in the order of preference — the most preferrable type are first. These would be "HTML5" videos. Flash videos would be less preferrable and they would be placed in the end of the array. Our example code will handle these correctly.

{
	"article": "<div><p>\n    <strong>Pour évaluer une vidéo, vous devez la louer.</strong>\n  </p></div>",
	"description": "A few pointers from my experience on things one should either do or avoid while in Finland. So some Finnish do's and don'ts! :) *On a side note, I know I hav...",
	"favicon": {
		"safe": "https%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Ffavicon-vflz7uhzw.ico",
		"url": "https://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico"
	},
	"html": "<div class=\"card\">\n    <iframe style=\"width: 720px; height: 405px\" src=\"https://www.youtube.com/embed/9N0HB8vMJrI\"></iframe>\n    \n    <div class=\"card-text\">\n      <h3>Do's and Don'ts in Finland | KatChats</h3>\n      <p>A few pointers from my experience on things one should either do or avoid while in Finland. So some Finnish do's and don'ts! :) *On a side note, I know I hav...</p>\n      \n        <p class=\"fav\">\n          <img onerror=\"this.style.display='none'\" class=\"favicon\" src=\"https://s.ytimg.com/yts/img/favicon-vflz7uhzw.ico\">\n          YouTube\n        </p>\n    </div>\n  \n  </div>",
	"images": [
		{
			"safe": "https%3A%2F%2Fi.ytimg.com%2Fvi%2F9N0HB8vMJrI%2Fmaxresdefault.jpg",
			"url": "https://i.ytimg.com/vi/9N0HB8vMJrI/maxresdefault.jpg"
		}
	],
	"oembed": {
		"author_name": "KatChats",
		"author_url": "https://www.youtube.com/user/adamLsidney",
		"height": 270,
		"html": "<iframe width=\"480\" height=\"270\" src=\"https://www.youtube.com/embed/9N0HB8vMJrI?feature=oembed\" frameborder=\"0\" allowfullscreen></iframe>",
		"provider_name": "YouTube",
		"provider_url": "https://www.youtube.com/",
		"thumbnail_height": 360,
		"thumbnail_url": "https://i.ytimg.com/vi/9N0HB8vMJrI/hqdefault.jpg",
		"thumbnail_width": 480,
		"title": "Do's and Don'ts in Finland | KatChats",
		"type": "video",
		"version": "1.0",
		"width": 480
	},
	"site": "YouTube",
	"title": "Do's and Don'ts in Finland | KatChats",
	"type": "video",
	"url": "https://www.youtube.com/watch?v=9N0HB8vMJrI",
	"videos": [
		{
			"height": "720",
			"secureUrl": "https://www.youtube.com/embed/9N0HB8vMJrI",
			"type": "text/html",
			"url": "https://www.youtube.com/embed/9N0HB8vMJrI",
			"width": "1280"
		},
		{
			"height": "720",
			"secureUrl": "https://www.youtube.com/v/9N0HB8vMJrI?version=3&autohide=1",
			"type": "application/x-shockwave-flash",
			"url": "http://www.youtube.com/v/9N0HB8vMJrI?version=3&autohide=1",
			"width": "1280"
		},
		{
			"height": "720",
			"type": "text/html",
			"url": "https://www.youtube.com/embed/9N0HB8vMJrI",
			"width": "1280"
		}
	]
}

3.3 An URL from Twitter.com

Here we can decide if we want to render this as an oEmbed or a regular embed, because the JSON contains both. Below you can see how it looks like when rendered in both ways.

{
	"url": "https://twitter.com/CatVlDEOS/status/1185826827333525504",
	"site": "Twitter",
	"favicon": {
		"url": "https://abs.twimg.com/icons/apple-touch-icon-192x192.png",
		"safe": "https%3A%2F%2Fabs.twimg.com%2Ficons%2Fapple-touch-icon-192x192.png"
	},
	"title": "Cat Pics & Videos 🐈 on Twitter",
	"description": "Don't touch my toy hooman 😠 https://t.co/2Le3GSjORL",
	"images": [
		{
			"url": "https://pbs.twimg.com/profile_images/1049047193083138049/3gLZVrmP.jpg",
			"safe": "https%3A%2F%2Fpbs.twimg.com%2Fprofile_images%2F1049047193083138049%2F3gLZVrmP.jpg",
			"width": 400,
			"height": 400
		}
	],
	"videos": [],
	"audios": [],
	"type": "rich",
	"oembed": {
		"url": "https://twitter.com/CatVlDEOS/status/1185826827333525504",
		"author_name": "Cat Pics & Videos 🐈",
		"author_url": "https://twitter.com/CatVlDEOS",
		"html": "<blockquote class=\"twitter-tweet\"><p lang=\"en\" dir=\"ltr\">Don&#39;t touch my toy hooman 😠 <a href=\"https://t.co/2Le3GSjORL\">pic.twitter.com/2Le3GSjORL</a></p>&mdash; Cat Pics &amp; Videos 🐈 (@CatVlDEOS) <a href=\"https://twitter.com/CatVlDEOS/status/1185826827333525504?ref_src=twsrc%5Etfw\">October 20, 2019</a></blockquote>\n<script async src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>\n",
		"width": 550,
		"height": null,
		"type": "rich",
		"cache_age": "3153600000",
		"provider_name": "Twitter",
		"provider_url": "https://twitter.com",
		"version": "1.0"
	},
	"duration": 395,
	"html": "<div class=\"card\">\n    <a target=\"_blank\" href=\"https://twitter.com/CatVlDEOS/status/1185826827333525504\">\n      \n    <img onerror=\"this.style.display='none'\" class=\"card-image\" src=\"https://pbs.twimg.com/profile_images/1049047193083138049/3gLZVrmP.jpg\">\n  \n      \n    <div class=\"card-text\">\n      <h3>Cat Pics & Videos 🐈 on Twitter</h3>\n      <p>Don't touch my toy hooman 😠 https://t.co/2Le3GSjORL</p>\n      \n        <p class=\"fav\">\n          <img onerror=\"this.style.display='none'\" class=\"favicon\" src=\"https://abs.twimg.com/icons/apple-touch-icon-192x192.png\">\n          Twitter\n        </p>\n    </div>\n  \n    </a>\n    </div>"
}

Here is the oEmbed rendering:

And here the regular:

4. Notes on card and image sizes

4.1 Controlling image size with CSS

You can control the image sizes with CSS. On the home page you saw the city card in two different sizes. The smaller had this CSS for the image:

.card img.card-image {float: left;max-width: 250px;max-height: 125px;}

The bigger had this CSS:

.card.card-big img.card-image {max-width: 100%;max-height: 600px;margin-bottom: 10px;}

You may also want to create square images, while maintaining the original aspect ratio, which you can do like this:

.card.square img.card-image {width: 150px;height: 150px;object-fit: cover;}

Word of caution; if the image is very tall and narrow or wide and shallow, the cropped area may not reflect what's in the image. Here's the example of the same aeroplane in a cropped square form:

4.2 Using predefined image size + fade-in

Since our service returns the image sizes automatically, you can do something useful with that information: you can calculate a placeholder for the image, which will be correctly sized before the image has even been loaded. This way the card will have its final form right in the beginning, and there won't be any shuffling happening in the browser.

As an extra bonus, you can show the image with a fade-in effect, which kicks in when the image has actually been loaded. Note that this requires some JavaScript.

We have implemented the predefined size and the fade-in effect on this website. Also our example apps will have the source code for theses, so you can check how it is done and borrow the most useful parts into your application.

4.2.1 Non-JavaScript support

The fade-in effect requires some JavaScript. This means that the images are initially hidden and only shown when they are loaded (the load event). The images will have opacity set to 0 to make them hidden, and opacity will be set to 1 in the image's load event. In order to show the images at all times, even when JavaScript is disabled, you can put this code in the head element of the page:

<noscript><style type="text/css">.card img.card-image {opacity: 1 !important;}</style></noscript>

6. Screenshots

You can get also screenshots with a different API endpoint:

https://api.embed.rocks/screenshot/?url=<url>&size=600x800

The screenshot is returned as base64 encoded PNG image, that is, as a string in the resulting JSON object.

You can put this straight into an IMG like this: <img src="data:image/png;base64,RESULT-DATA-HERE"> tag or you can save it into disk after converting into binary (with JavaScript it would go like this: require('fs').writeFileSync('pic.png', Buffer.from(json.src, 'base64'));).

The size parameter is optional.

Your API key should be sent in the headers as x-api-key.

7. Example code

We have two example apps for integration: one with plain JavaScript and one with a templating engine. Use whichever suits you best, or even both. You can use these as a starting point for your own implementation.

In case you want to use Nginx to serve images safely, here is our config file for that: NGINX.conf. You can find examples how to use that in the eintegration examples above.