Projects / static-instagram-embed

static-instagram-embed

PythonJavaScriptWeb Components

Background

I was involved in building a website for a friend's business. We wanted to avoid cookie banners entirely, so we used Plausible for GDPR-compliant analytics, OpenStreetMap instead of Google Maps, and no CDNs - all assets self-hosted. But we also wanted to show the most recent Instagram posts on the site.

Instagram's official embeds likely track visitors the same way Facebook Like buttons do, and they don't look great either. So I built a custom solution.

The embed component integrated into a business website, showing recent Instagram posts in a grid

How it works

The project has two parts:

  1. A Python script that fetches the recent posts from an Instagram profile and stores the data on your server. You run this in a cron job to keep it updated.
  2. A Web Component (built with Stencil) that you embed in your site. It loads the stored data and displays the posts - the visitor's browser never makes any request to Instagram. It works in any framework or with no framework at all.

Using it is just a script tag and a custom element:

<script type="module" src="open-instagram-embed.min.js"></script>

<open-instagram-embed
  datasource="url-to/insta-data"
  count="3"
></open-instagram-embed>

The component shows the post thumbnails in a grid. Hovering a post reveals an overlay with the like count and either the comment count or view count (for videos). A small icon in the upper corner indicates the content type (video or multi-image carousel). Clicking a post opens it on Instagram.

Data format

The fetch script produces an insta-data/ directory containing a data.json file and thumbnail images for each post. The JSON is a simple array:

type Post = {
  shortcode: string,
  type: "GraphImage" | "GraphSidecar" | "GraphVideo",
  thumbnail_image: string,
  likes: number,
  views: number | null,
  comments: number
}

This format is deliberately simple. If the bundled fetch script doesn't work for you (Instagram is aggressive about rate limits and IP blocks), you can write your own or use a scraper service - the component only cares about the output format.

Fetching the data

The original script uses instaloader, which works well but can hit Instagram's rate limits, especially from servers.

While writing this page, I needed fresh post data for the screenshots but instaloader didn't work without an account to log in with. So I sat down with an LLM and we wrote a variant of the fetch script that uses a popular scraper-as-a-service platform1. It's much easier and more reliable than scraping Instagram yourself. Each scrape costs about a cent, and new accounts typically get free credits that last a long time for this use case (fetching a handful of posts occasionally). The component doesn't care where the data comes from, so any scraper service (or your own script) will do.

Status

The business that originally used this didn't work out, so I don't actively use it myself (I don't use Instagram). I have confirmed that the web component still works perfectly. The problem of embedding Instagram without tracking is common enough that it might be useful to others - the project is available on GitHub.


  1. In this case Apify. Worked really well for me, though I haven't compared alternatives.