DEV Community

Mobeen Ahmed
Mobeen Ahmed

Posted on

Facebook Reel Extraction Quirks I Ran Into

When I added Facebook to ramrom.io, I assumed it would be the easy one. TikTok had taught me to expect weirdness, Instagram had its expiring links, but Facebook felt like a normal website with normal video pages. It was not. Here are the quirks I actually ran into, and what I did about each of them in plain PHP.

Reels and videos are not the same thing

The first surprise was that a Facebook reel and a Facebook video are different beasts behind the scenes, even though they look similar to a visitor. They come back from the provider in different shapes: different field names for the video file, different ways of reporting quality, and sometimes a reel simply has no audio track at all.

I stopped treating them as one input. My resolve endpoint now detects which kind of link it was given first — a /reel/ URL, a /watch/ URL, or a /videos/ URL — and then normalises whatever the provider returns into one shape before the page ever sees it. One shape in, one shape out. Everything downstream, the quality dropdown, the download button, the error message, only ever deals with that one shape.

Short links need expanding first

People paste fb.watch links constantly, and those are just redirects. If you hand a short link straight to the extraction API, it often comes back empty, because the API never sees the real video page. So before I spend an API call, I expand the link: follow the redirects with cURL, cap it at five hops, and take the final URL. That one step fixed a whole class of "it works for me but not for them" reports, because the difference was usually just which form of the link they had copied.

The file you get is not always the file you asked for

The provider sometimes returns a URL that looks like a video and is not one. It might be an error page, a playlist, or a lower-quality fallback served with the wrong label. I learned to stop trusting the label. Before I show a download button, I check the response headers: is the content type actually video, and is the file size a plausible number rather than a few kilobytes? If either check fails, the visitor gets an honest message instead of a broken download. Checking costs one HEAD request. A visitor downloading an HTML file renamed to .mp4 costs far more trust.

Private and deleted reels fail silently

This one cost me the most time. A reel that has been deleted, or one the owner set to friends-only, does not produce a clean error from the provider. It produces an empty result, or a generic failure that looks exactly like the provider being down. I could not fix that at the source, so I split the two cases apart as best I could: if the provider is answering normally for other links but returns nothing for this one, I say the video is probably deleted or private. It is a guess, and I say so on the page. A plain "this link did not work, and here is the most likely reason" beats a spinner that never ends.

One provider per platform, in the end

Facebook started life on the same all-in-one provider as everything else. That provider had bad afternoons where every platform failed at once. I moved Facebook onto its own dedicated service, so a bad day for one provider no longer takes the whole site down. The resolve endpoint still retries up to three times with a short pause between attempts, because flaky APIs are a fact of life, but the retry loop is a safety net now, not the whole plan.

The lesson

None of these quirks is hard on its own. The work is in expecting them: normalise the input before you spend an API call, verify the output before you show a button, and when something fails, say which kind of failure it most likely is. That is most of what running a small downloader really is — not clever code, just patient plumbing around other people's edges.

Top comments (0)