Why I Built EasyDown: Normalizing Public Media Across Social Platforms

โ—1 โ—4
calendar_today ago โ€ข schedule4 min read

I started building EasyDown after running into the same integration problem repeatedly: a user would paste a perfectly valid public social media URL, but every platform represented the media differently. A Reel was not shaped like a TikTok post, an Instagram carousel was not shaped like a Bilibili video, and even two URLs from the same platform could require different handling.

The first version looked simple. Detect the host, call a platform-specific parser, and return a video URL. That design stopped working as soon as I tested real posts. Some posts contained images rather than video. Some were mixed carousels. Some video formats separated audio and video. CDN links expired. Browser requests failed because of CORS or missing headers. A profile URL looked similar to a post URL but represented a completely different resource.

I decided that the useful product was not another collection of one-off download scripts. I needed a stable backend contract that could represent media from several public platforms without forcing every client to learn each upstream response format.

The problem I wanted the API to solve

From an application developer's perspective, the input should be one supported public post URL. The result should be a predictable object containing the platform, title, thumbnail, duration when available, and arrays for images, videos, and audio.

The arrays matter. Assuming that one post equals one MP4 is an easy mistake. An Instagram carousel may contain several photos and videos in a meaningful order. A TikTok photo post may have no video at all. A Bilibili response may expose multiple stream options. Returning a collection lets the product decide whether to show a picker, enqueue every item, or select a preferred rendition.

I also wanted failures to be explicit. Private posts, deleted media, expired Stories, login-only pages, profiles, live content, and region-restricted items should not be presented as successful parses. A client needs to distinguish an invalid input from a temporary upstream failure and from a valid post that has no downloadable media.

One normalized endpoint and platform-specific endpoints

EasyDown now has a common endpoint for applications that want one stable media model across supported networks. It also has platform-specific endpoints for integrations that need additional public fields from a particular service.

Instagram is a good example. The normalized media object is enough for a download workflow, but some applications also need the public shortcode, caption, author fields, image candidates, video renditions, or carousel items. I documented the current request and response contract in the Instagram API documentation I maintain.

The platform request is a normal authenticated POST from a backend:

import requests

response = requests.post(
    "https://api.easydown.org/api/v1/platforms/instagram/parse",
    headers={
        "Authorization": "Bearer ed_live_your_token",
        "Content-Type": "application/json",
    },
    json={
        "url": "https://www.instagram.com/reel/DY2PcmxiBE2/"
    },
    timeout=30,
)

response.raise_for_status()
payload = response.json()
media = payload["data"]["media"]

for item in media.get("videos", []):
    print(item["url"], item.get("quality"), item.get("mimeType"))

The token stays on the server. I do not recommend placing a production token in browser JavaScript, a URL, analytics, or a public repository.

Platforms I support

The current API covers public post URLs from TikTok, Douyin, Toutiao, Instagram, Threads, Xiaohongshu or RedNote, Bilibili, Kuaishou or Kwai, Weibo, X or Twitter, and YouTube.

Support does not mean scraping every page on those sites. The contract is intentionally scoped to documented single-post URL formats and compatible share links. Profiles, search pages, private messages, private media, arbitrary bulk collection, and unavailable content are outside that scope.

Keeping the boundary narrow has made the API easier to reason about. Every new URL form has to pass through validation, platform detection, parsing, normalization, sensitive-field filtering, and media checks before the response is considered successful.

Lessons from building the media layer

The hardest production issue has been treating returned media URLs as temporary resources. Many social media CDNs use signatures or expiration parameters. Storing the URL as though it were a permanent asset creates jobs that fail hours or days later.

My current rule is to keep the original public post URL as the durable reference. A backend parses it when the application needs fresh media candidates, then downloads or proxies the selected file while it is valid. When a CDN URL expires, the application re-parses the post instead of retrying the expired URL indefinitely.

The second lesson is that browser playback and backend downloading are different operations. A URL that opens in one browser session can still fail in application code because of CORS, CORP, cookies, Referer requirements, or a short-lived signature. Media fetching belongs in the backend when the product needs predictable behavior.

The third lesson is to normalize conservatively. I keep stable cross-platform fields small and predictable, while versioning the extra platform data separately. That approach gives client applications a dependable base model without discarding useful public fields that only exist on one platform.

What I would test before shipping

For an Instagram integration, I would test a public Reel, a video post, a single-image post, a mixed carousel, a compatible share URL, and an unavailable post. I would also test an expired CDN URL and confirm that the application re-parses the original post.

Across every platform, I check that credentials remain server-side, errors do not leak tokens, media arrays can be empty, and the UI does not promise access to private or protected content. Those cases are less exciting than the first successful request, but they decide whether an integration survives real users.

Building EasyDown has mostly been an exercise in turning many unstable upstream shapes into one smaller contract that an application can trust. The parsing code still changes as platforms change. The goal of the API is to keep those changes behind a stable boundary so client code does not have to change with them.

1 Comment

0 votes
๐Ÿ”ฅ Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

The Zero-Net-Loss Fleet & The Mercenary Squad: A Live AI Economy

DEVPlank - Aug 4

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

How I Built a React Portfolio in 7 Days That Landed โ‚น1.2L in Freelance Work

Dharanidharan - Feb 9

Dashboard Operasional Armada Rental Mobil dengan Python + FastAPI

Masbadar - Mar 12

I Wrote a Script to Fix Audible's Unreadable PDF Filenames

snapsynapseverified - Apr 20
chevron_left
143 Points โ€ข 5 Badges
1Posts
0Comments
lazy person

Related Jobs

View all jobs โ†’

Commenters (This Week)

2 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!