Instagram API architecture: every call behind a like, a comment and a story
Every call behind a follow, a like and a comment on Virat Kohli's post, then one story uploaded in chunks, copied into 184 friends' trays and read back.
A fan and a star
Here’s a fan and a star, call by call. Rajat opens Virat Kohli’s profile. The gateway checks his login and his rate limit, and passes it to Graph.
Graph reads the profile from Memcache, never the database: it’s read millions of times a day. Back come 273 million followers, and links to photos on the CDN.
He taps Follow, and the app sends one small request. Graph writes two rows in PostgreSQL: Virat in Rajat’s following, and Rajat in Virat’s followers, each on its own user’s shard.
The app hears back, and the button says Following. Graph also tells Notify, which gathers minutes of follows into one line for Virat: Rajat and 48 others.
Now his story. The app asks Story, which reads Virat’s list from Memcache: one list that every follower reads, never copied into 273 million trays.
Back comes one story, and its video streams from the CDN. Once he has watched it, his app tells Story, in the background, without waiting.
Story adds one viewer row in Cassandra. A view is a row, never a notification.
He double-taps the post. The heart turns red at once, and Like writes one row: this post, this person. Tap again and the row is already there, so a like never counts twice.
Then it adds one to a counter, one of sixty-four that are summed whenever the count is read. The app gets the new count.
Notify groups the like with the rest: Rajat and 12,940 others liked your post.
Last, a comment: Take a bow, King. Comment asks Safety to check it for spam, abuse, and words Virat has chosen to hide.
Safety finds nothing to hide, and answers clean. So Comment saves it in PostgreSQL, under Virat’s post, and the answer comes back to the app: Rajat’s comment appears.
And Notify sends Virat one more grouped line: Rajat and 1,830 others commented.
Twenty-three calls: a profile read from cache, a like that counts once, and every alert to Virat grouped.
Every call, in order
Rajat → Graph: GET /users/virat.kohli — Bearer tk_rj · the gateway checks his login and rate limit (REST · HTTPS)
Graph → Graph: Memcache · profile:virat — hit · read millions of times a day · the database rests (Internal RPC)
Graph → Rajat: 200 · 273M followers — { bio, 12 photo URLs } · the photos come from the CDN, near him (reply, REST · HTTPS)
Rajat → Graph: POST /follows — { followee: virat.kohli } (REST · HTTPS)
Graph → Graph: 2 rows · PostgreSQL — Rajat’s following, on his shard · Virat’s followers, on Virat’s · count + 1 (Internal RPC)
Here’s one story and who sees it, call by call. Rajat’s photo goes up to Media in small chunks, so a weak signal resends only the chunk it lost.
Media keeps the original in object storage, makes the smaller sizes phones download, and answers with an upload id. The photo is stored, but it isn’t a story yet.
So the app asks Story to post it, and Story asks Safety to check the photo before anyone can see it.
It’s clean, so Story saves it with a twenty-four-hour expiry: after that, it leaves every tray, and only Rajat can see it, in his archive. The answer comes back, and a ring appears around his photo.
Story puts a Story Posted event on the stream, and the fan-out worker that picks it up asks Graph who follows Rajat.
184 people. It writes the story into each of their trays, the row of stories at the top of the app: 184 small writes, done in moments.
DK opens the app. No notification came: his app asks for his tray, already filled, so it’s one quick read, and Rajat’s ring comes first.
He taps it. The photo comes from the CDN, and his app tells Story he has seen it. Faf, ABD, Chris and Virat do the same: each view is one small write to Rajat’s viewers.
Later, Rajat opens his story and asks who has watched. Five rows come back: seen by five.
Eighteen calls: a photo stored once, copied into 184 trays, and read in moments. For a star, that copying would take hours, so a star’s stories are fetched instead.
Every call, in order
Rajat → Media: POST /uploads · 8 chunks — the RCB win · a lost chunk is sent again, alone (REST · HTTPS)
Media → Media: original + 3 sizes — original → object storage · the sizes phones download (Internal RPC)
Media → Rajat: 201 · upload u_93 — stored · not a story yet (reply, REST · HTTPS)
Rajat → Story: POST /stories — { upload: u_93 } (REST · HTTPS)
Story → Safety: check photo — before anyone can see it (Internal RPC)