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

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>&key=<key>

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

Request params

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.

You can add &skip=property1,property2 to the query, which will tell, that you don't care about of certain properties. This will speed up the API call. The properties that can be skipped are:

article
description
oembed
imextra

In particular, skipping article often speeds up the query the most. You can add them all, if you really care about the speed, as &skip=article,description,oembed,imextra.

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

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
favicon{
  • url: url of the favicon
  • safe: the same url as urlencoded component
}
images[ {
  • url: url of the image
  • safe: the same url as 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 urlencoded component
}
videos[ {
  • url: url of the video
  • safe: the same url as 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 urlencoded component
}
audios[ {
  • url: url of the audio
  • safe: the same url as urlencoded component
} ]

type: photo

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

type: error

NameValue
typeerror
errorError object
msgError message

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.

{
	"article": "<div><p>\n      Twitter may be over capacity or experiencing a momentary hiccup. <a href=\"#\">Try again</a> or visit <a href=\"http://status.twitter.com\">Twitter Status</a> for more information.\n    </p></div>",
	"description": "“to understand the pillow, one must become the pillow”",
	"favicon": {
		"safe": "https%3A%2F%2Fabs.twimg.com%2Ffavicons%2Ffavicon.ico",
		"url": "https://abs.twimg.com/favicons/favicon.ico"
	},
	"html": "<div class=\"card\">\n    <a target=\"_blank\" href=\"https://twitter.com/EmrgencyKittens/status/817189457476648960\">\n      \n    <img onerror=\"this.style.display='none'\" class=\"card-image\" src=\"https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FC1c9erlWgAAUhEO.jpg%3Alarge\">\n  \n      \n    <div class=\"card-text\">\n      <h3>Emergency Kittens on Twitter</h3>\n      <p>“to understand the pillow, one must become the pillow”</p>\n      \n        <p class=\"fav\">\n          <img onerror=\"this.style.display='none'\" class=\"favicon\" src=\"https://abs.twimg.com/favicons/favicon.ico\">\n          Twitter\n        </p>\n    </div>\n  \n    </a>\n    </div>",
	"images": [
		{
			"safe": "https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FC1c9erlWgAAUhEO.jpg%3Alarge",
			"url": "https://pbs.twimg.com/media/C1c9erlWgAAUhEO.jpg:large"
		}
	],
	"oembed": {
		"author_name": "Emergency Kittens",
		"author_url": "https://twitter.com/EmrgencyKittens",
		"cache_age": "3153600000",
		"height": null,
		"html": "<blockquote class=\"twitter-tweet\"><p lang=\"en\" dir=\"ltr\">to understand the pillow, one must become the pillow <a href=\"https://t.co/UUqd0uJQht\">pic.twitter.com/UUqd0uJQht</a></p>&mdash; Emergency Kittens (@EmrgencyKittens) <a href=\"https://twitter.com/EmrgencyKittens/status/817189457476648960\">January 6, 2017</a></blockquote>\n<script async src=\"//platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>",
		"provider_name": "Twitter",
		"provider_url": "https://twitter.com",
		"type": "rich",
		"url": "https://twitter.com/EmrgencyKittens/status/817189457476648960",
		"version": "1.0",
		"width": 550
	},
	"site": "Twitter",
	"title": "Emergency Kittens on Twitter",
	"type": "rich",
	"url": "https://twitter.com/EmrgencyKittens/status/817189457476648960",
	"videos": []
}

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 he 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. 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.