DEV Community

Sebastian Casvean
Sebastian Casvean

Posted on • Originally published at zenndra.com

How to Embed Medium Articles on Your Website (Without Scrapers)

How to Embed Medium Articles on Your Website (Without Scrapers)

Every “read on Medium” link is a leak: you lose session time, brand control, and often search visibility because Google treats outbound teasers differently from full content on your domain.

This guide is a production playbook for embedding Medium posts on your site—architecture first, then code you can paste into a Node or edge function.

Tool outcome: By the end you have a small article cache + render pipeline you can drop behind React, Astro, or WordPress.


Why scraping Medium HTML fails

Teams often start with fetch(mediumUrl) and cheerio. It works until Medium ships a layout change and your pipeline returns empty <article> nodes at 2 a.m.

A durable approach treats Medium as a content source with stable identifiers (article_id), not as a DOM you own.


What a real embed looks like

A real embed is not a favicon + title link. It is the full post—headings, images, code blocks—inside your layout, nav, and CTA.

Format Best for
HTML Drop-in rendering in React, Next.js, WordPress themes
Markdown Hugo, Astro, Eleventy, static pipelines

Pattern that scales:

  1. Catalog — discover article_id values (search, user feed, editorial list).
  2. Content — fetch body once; store in DB, S3, or edge KV.
  3. Render — serve from cache on page views (no API call per visitor).

Step 1 — Discover articles

Example: list posts for a writer (replace handle after resolving user_id—see find Medium writers and user IDs).

const API = 'https://api.zenndra.com';
const headers = { Authorization: `Bearer ${process.env.ZENNDRA_API_KEY}` };

// After you have user_id from /user/id_for/{username}
const userId = 'YOUR_USER_ID';
const listRes = await fetch(`${API}/user/${userId}/articles`, { headers });
const { articles } = await listRes.json();
// articles[].id → article_id for step 2
Enter fullscreen mode Exit fullscreen mode

Step 2 — Fetch HTML and cache it

const articleId = 'f5ef1da2850d';

const res = await fetch(`${API}/article/${articleId}/html`, { headers });
if (!res.ok) throw new Error(`Medium API: ${res.status}`);
const { html } = await res.json();

// Persist html keyed by articleId (Redis, Postgres, R2, etc.)
await cache.set(`article:${articleId}:html`, html, { ttl: 86400 });
Enter fullscreen mode Exit fullscreen mode

For static sites, swap /html for /markdown and write files into content/posts/.


Step 3 — Render safely

  • Run HTML through a sanitizer (DOMPurify in the browser, or server-side equivalent) if untrusted authors exist.
  • Map Medium classes to your design tokens once in CSS.
  • Keep a visible “Originally on Medium” link—good for authors and trust.

What to avoid

  • DOM scrapers that break silently when markup shifts.
  • Title-only embeds—users and crawlers notice.
  • Fetching on every request—burns latency and API quota.

Keywords this solves

embed medium articles, medium article html, medium api alternative, syndicate medium blog, medium seo on own domain.


Further reading

Top comments (0)