diff --git a/config/schema/media_entity_instagram.schema.yml b/config/schema/media_entity_instagram.schema.yml
index 0c894ed..4248e30 100644
--- a/config/schema/media_entity_instagram.schema.yml
+++ b/config/schema/media_entity_instagram.schema.yml
@@ -22,11 +22,8 @@ media_entity.bundle.type.instagram:
 
 field.formatter.settings.instagram_embed:
   type: mapping
-  label: 'Image field display format settings'
+  label: 'Instagram field display format settings'
   mapping:
-    width:
-      type: integer
-      label: 'Width'
-    height:
-      type: integer
-      label: 'Height'
+    hidecaption:
+      type: boolean
+      label: 'Caption hidden'
diff --git a/media_entity_instagram.libraries.yml b/media_entity_instagram.libraries.yml
new file mode 100644
index 0000000..5b25e3a
--- /dev/null
+++ b/media_entity_instagram.libraries.yml
@@ -0,0 +1,8 @@
+instagram.embeds:
+  remote: //platform.instagram.com/en_US/embeds.js
+  version: 1.0.0
+  license:
+    name: MIT
+    gpl-compatible: true
+  js:
+    //platform.instagram.com/en_US/embeds.js: { type: external, minified: true }
diff --git a/media_entity_instagram.module b/media_entity_instagram.module
index b3d9bbc..aa6dcb2 100644
--- a/media_entity_instagram.module
+++ b/media_entity_instagram.module
@@ -1 +1,13 @@
 <?php
+
+/**
+ * Implements hook_theme().
+ */
+function media_entity_instagram_theme() {
+  return [
+    'media_entity_instagram_post' => [
+      'variables' => ['post' => NULL],
+    ],
+  ];
+}
+
diff --git a/media_entity_instagram.services.yml b/media_entity_instagram.services.yml
new file mode 100644
index 0000000..2b98197
--- /dev/null
+++ b/media_entity_instagram.services.yml
@@ -0,0 +1,13 @@
+services:
+  media_entity_instagram.instagram_embed_fetcher:
+    class: '\Drupal\media_entity_instagram\InstagramEmbedFetcher'
+    arguments:
+      - '@media_entity_instagram.cache.instagram_posts'
+
+  media_entity_instagram.cache.instagram_posts:
+    class: '\Drupal\Core\Cache\CacheBackendInterface'
+    tags:
+      - { name: cache.bin, default_backend: cache.backend.chainedfast }
+    factory: cache_factory:get
+    arguments:
+      - instagram_posts
diff --git a/src/InstagramEmbedFetcher.php b/src/InstagramEmbedFetcher.php
new file mode 100644
index 0000000..8b7c0a6
--- /dev/null
+++ b/src/InstagramEmbedFetcher.php
@@ -0,0 +1,90 @@
+<?php
+
+namespace Drupal\media_entity_instagram;
+
+use Drupal\Component\Serialization\Json;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Component\Utility\UrlHelper;
+
+/**
+ * Fetches (and caches) instagram post data from free to use Instagram's oEmbed call.
+ */
+class InstagramEmbedFetcher implements InstagramEmbedFetcherInterface {
+
+  /**
+   * The optional cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cache;
+
+  /**
+   * The current set of Instagram's oEmbed options.
+   *
+   * @var array
+   */
+  protected $options = [];
+
+  /**
+   * InstagramEmbedFetcher constructor.
+   *
+   * @param \Drupal\Core\Cache\CacheBackendInterface|NULL $cache
+   *   (optional) A cache bin for storing fetched instagram posts.
+   */
+  public function __construct(CacheBackendInterface $cache = NULL) {
+    $this->cache = $cache;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fetchInstagramEmbed($shortcode) {
+    // Tweets don't change much, so pull it out of the cache (if we have one)
+    // if this one has already been fetched.
+    if ($this->cache && $cached_instagram_post = $this->cache->get($shortcode)) {
+      return $cached_instagram_post->data;
+    }
+
+    try {
+      $options = $this->getOptions();
+      $queryParameter = UrlHelper::buildQuery($options);
+
+      $client = \Drupal::httpClient();
+      $response = $client->request('GET', 'http://api.instagram.com/oembed?' . $queryParameter);
+
+      if ($response->getStatusCode() === 200) {
+        $response = Json::decode($response->getBody()->getContents());
+      }
+    } catch(\Exception $e) {
+      throw new \Exception("Could not retrieve tweet $shortcode.");
+    }
+
+    // If we have a cache, store the response for future use.
+    if ($this->cache) {
+      // Instagram posts don't change often, so the response should expire from the cache
+      // on its own in 90 days.
+      $this->cache->set($shortcode, $response, time() + (86400 * 90));
+    }
+
+    return $response;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOptions() {
+    return $this->options;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOptions($shortcode, $hidecaption = FALSE, $omitscript = TRUE) {
+    $this->options = [
+      'url' => 'http://instagr.am/p/' . $shortcode . '/',
+      'hidecaption' => $hidecaption,
+      'omitscript' => $omitscript,
+    ];
+  }
+
+}
diff --git a/src/InstagramEmbedFetcherInterface.php b/src/InstagramEmbedFetcherInterface.php
new file mode 100644
index 0000000..b9f6712
--- /dev/null
+++ b/src/InstagramEmbedFetcherInterface.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Drupal\media_entity_instagram;
+
+/**
+ * Defines a wrapper around the Instagram oEmbed call.
+ */
+interface InstagramEmbedFetcherInterface {
+
+  /**
+   * Retrieves a instagram post by its shortcode.
+   *
+   * @param int $shortcode
+   *   The instagram post shortcode.
+
+   * @return array
+   *   The instagram oEmbed information.
+   */
+  public function fetchInstagramEmbed($shortcode);
+
+  /**
+   * Returns the current Instagram's oEmbed call options .
+   *
+   * @return array
+   *   The oEmbed call options. Will be an array with url, hidecaption and omitscript elements.
+   */
+  public function getOptions();
+
+  /**
+   * Sets the options for Instagram's oEmbed call.
+   *
+   * @param string $shortcode
+   *   The shortcode for URL option.
+   * @param bool $hidecaption
+   *   The hidecaption option.
+   * @param bool $omitscript
+   *   The omitscript option.
+   */
+  public function setOptions($shortcode, $hidecaption, $omitscript);
+
+}
diff --git a/src/Plugin/Field/FieldFormatter/InstagramEmbedFormatter.php b/src/Plugin/Field/FieldFormatter/InstagramEmbedFormatter.php
index ea86b38..d3cf7c9 100644
--- a/src/Plugin/Field/FieldFormatter/InstagramEmbedFormatter.php
+++ b/src/Plugin/Field/FieldFormatter/InstagramEmbedFormatter.php
@@ -7,6 +7,7 @@ use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\media_entity\EmbedCodeValueTrait;
 use Drupal\media_entity_instagram\Plugin\MediaEntity\Type\Instagram;
+use Drupal\media_entity_instagram\InstagramEmbedFetcher;
 
 /**
  * Plugin implementation of the 'instagram_embed' formatter.
@@ -31,29 +32,36 @@ class InstagramEmbedFormatter extends FormatterBase {
     $settings = $this->getSettings();
     foreach ($items as $delta => $item) {
       $matches = [];
+
       foreach (Instagram::$validationRegexp as $pattern => $key) {
-        if (preg_match($pattern, $this->getEmbedCode($item), $matches)) {
-          break;
+        if (preg_match($pattern, $this->getEmbedCode($item), $item_matches)) {
+          $matches[] = $item_matches;
         }
       }
 
+      if (!empty($matches)) {
+        $matches = reset($matches);
+      }
+
       if (!empty($matches['shortcode'])) {
+        $instagram = new InstagramEmbedFetcher(\Drupal::service('media_entity_instagram.cache.instagram_posts'));  // TODO: Find a better way to activate caching.
+        $instagram->setOptions($matches['shortcode'], $settings['hidecaption']);  // TODO: Add to construct or create method.
+        $instagram = $instagram->fetchInstagramEmbed($matches['shortcode']);
         $element[$delta] = [
-          '#type' => 'html_tag',
-          '#tag' => 'iframe',
-          '#attributes' => [
-            'allowtransparency' => 'true',
-            'frameborder' => 0,
-            'position' => 'absolute',
-            'scrolling' => 'no',
-            'src' => '//instagram.com/p/' . $matches['shortcode'] . '/embed/',
-            'width' => $settings['width'],
-            'height' => $settings['height'],
-          ],
+          '#theme' => 'media_entity_instagram_post',
+          '#post' => (string) $instagram['html'],
         ];
       }
     }
 
+    if (!empty($element)) {
+      $element['#attached'] = [
+        'library' => [
+          'media_entity_instagram/instagram.embeds',
+        ],
+      ];
+    }
+
     return $element;
   }
 
@@ -62,8 +70,7 @@ class InstagramEmbedFormatter extends FormatterBase {
    */
   public static function defaultSettings() {
     return array(
-      'width' => '480',
-      'height' => '640',
+      'hidecaption' => FALSE,
     ) + parent::defaultSettings();
   }
 
@@ -73,20 +80,11 @@ class InstagramEmbedFormatter extends FormatterBase {
   public function settingsForm(array $form, FormStateInterface $form_state) {
     $elements = parent::settingsForm($form, $form_state);
 
-    $elements['width'] = array(
-      '#type' => 'number',
-      '#title' => $this->t('Width'),
-      '#default_value' => $this->getSetting('width'),
-      '#min' => 1,
-      '#description' => $this->t('Width of instagram.'),
-    );
-
-    $elements['height'] = array(
-      '#type' => 'number',
-      '#title' => $this->t('Height'),
-      '#default_value' => $this->getSetting('height'),
-      '#min' => 1,
-      '#description' => $this->t('Height of instagram.'),
+    $elements['hidecaption'] = array(
+      '#type' => 'checkbox',
+      '#title' => $this->t('Caption hidden'),
+      '#default_value' => $this->getSetting('hidecaption'),
+      '#description' => $this->t('Enable to hide caption of Instagram posts.'),
     );
 
     return $elements;
@@ -96,13 +94,9 @@ class InstagramEmbedFormatter extends FormatterBase {
    * {@inheritdoc}
    */
   public function settingsSummary() {
+    $settings = $this->getSettings();
     return [
-      $this->t('Width: @width px', [
-        '@width' => $this->getSetting('width'),
-      ]),
-      $this->t('Height: @height px', [
-        '@height' => $this->getSetting('height'),
-      ]),
+      $this->t('Caption: @hidecaption', ['@hidecaption' => $settings['hidecaption'] ? 'Hidden' : 'Visible']),
     ];
   }
 
diff --git a/src/Tests/InstagramEmbedFormatterTest.php b/src/Tests/InstagramEmbedFormatterTest.php
index 032aeea..975cf50 100644
--- a/src/Tests/InstagramEmbedFormatterTest.php
+++ b/src/Tests/InstagramEmbedFormatterTest.php
@@ -12,8 +12,6 @@ use Drupal\media_entity\Tests\MediaTestTrait;
  */
 class InstagramEmbedFormatterTest extends WebTestBase {
 
-  use MediaTestTrait;
-
   /**
    * Modules to enable.
    *
@@ -28,6 +26,8 @@ class InstagramEmbedFormatterTest extends WebTestBase {
     'block',
   );
 
+  use MediaTestTrait;
+
   /**
    * The test user.
    *
@@ -138,11 +138,21 @@ class InstagramEmbedFormatterTest extends WebTestBase {
     $this->drupalPostForm(NULL, $edit, t('Save'));
     $this->assertText('Your settings have been saved.');
 
+    // First set absolute size of the embed.
+    $this->drupalPostAjaxForm(NULL, [], 'field_embed_code_settings_edit');
+    $edit = [
+      'fields[field_embed_code][settings_edit_form][settings][hidecaption]' => '0',
+    ];
+    $this->drupalPostAjaxForm(NULL, $edit, 'field_embed_code_plugin_settings_update');
+    $this->drupalPostForm(NULL, [], t('Save'));
+    $this->assertText('Your settings have been saved.');
+    $this->assertText('Caption: Visible');
+
     // Create and save the media with an instagram media code.
     $this->drupalGet('media/add/' . $bundle->id());
 
     // Random image from instagram.
-    $instagram = '<blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="7" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:62.4537037037% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BGwoSD1hQQw/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">Weekend Hashtag Project: #WHParchitecture The goal this weekend is to photograph architecture, and will be curated by Shoair Mavlian (@shoair_m), photography curator of the Tate Modern museum (@tate) in London, which is celebrating the opening of its new building, the Switch House, today. “Architecture is present everywhere in our everyday surroundings — from cutting-edge museum buildings and towering glass office blocks to bus stops, schools and sports stadiums,” says Shoair. “The world looks very different if we stop, rethink (and photograph!) the everyday examples which surround us.” Here is how Shoair says to get started: Focus on the different ways buildings and landmarks affect you. “Architectural structures play an important role in our everyday lives, from the private spaces we share with family to communal spaces we work or study in to shared public spaces we use to gather and come together,” says Shoair. Step back to show buildings within the context of their landscapes and surroundings (whether a skyscraper towering over an urban skyline or a thatched-roof house in the middle of an open field). Don’t forget to go inside the spaces you photograph to capture interesting details in walls, doorways and stairwells — as well as the people that inhabit or utilize the space. PROJECT RULES: Please add the #WHParchitecture hashtag only to photos and videos taken over this weekend and only submit your own visuals to the project. If you include music in your submissions, please only use music to which you own the rights. Any tagged visual taken over the weekend is eligible to be featured next week. Photo of @tate by @freepy</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A photo posted by Instagram (@instagram) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-06-17T15:00:09+00:00">Jun 17, 2016 at 8:00am PDT</time></p></div></blockquote> <script async defer src="//platform.instagram.com/en_US/embeds.js"></script>';
+    $instagram = 'https://www.instagram.com/p/bNd86MSFv6/';  // example URL from https://www.instagram.com/developer/embedding/
 
     $edit = [
       'name[0][value]' => 'Title',
@@ -154,8 +164,10 @@ class InstagramEmbedFormatterTest extends WebTestBase {
     $this->assertText('Title');
     $this->assertText('Embed code');
 
-    // Assert that the formatter exists on this page.
+    // Assert that the formatter exists on this page and that it has absolute
+    // size.
     $this->assertFieldByXPath('//iframe');
+    $this->assertNoRaw('platform.instagram.com/en_US/embeds.js');
   }
 
 }
diff --git a/templates/media-entity-instagram-post.html.twig b/templates/media-entity-instagram-post.html.twig
new file mode 100644
index 0000000..604f750
--- /dev/null
+++ b/templates/media-entity-instagram-post.html.twig
@@ -0,0 +1,9 @@
+{#
+/**
+ * @file
+ * Default theme implementation to display a post.
+ *
+ * @ingroup themeable
+ */
+#}
+{{ post|raw }}
