Developers: Mbuyiselo & Busi Project: Instagram Feed API Stack: Node.js • Express.js • JavaScript • HTML/CSS Status: Planning / MVP Development Demo: Live Demo
The Instagram Feed API is a reusable backend service that allows websites to display the latest posts from an authorised client's Instagram account.
Our first use case is a photographer's portfolio website.
The website will use:
- HTML
- CSS
- JavaScript
The backend will use:
- Node.js
- Express.js
- JavaScript
Instead of putting Instagram credentials directly into the website, our backend will communicate with Instagram through Meta's supported Instagram API.
Client posts on Instagram
↓
Instagram
↓
Meta / Instagram API
↓
Our Instagram Feed API
↓
Client Website
The long-term goal is to make the API reusable so that future client websites can use the same system.
A photographer may currently:
- Upload new work to Instagram.
- Update their Instagram page.
- Manually upload the same work to their website.
We want to reduce that duplication.
Instead:
Photographer posts
↓
Instagram
↓
Our API
↓
Website
The website can retrieve recent Instagram content automatically.
We should never place secret Instagram credentials directly inside frontend JavaScript.
For example:
const token = "SECRET_INSTAGRAM_TOKEN";would expose the credential to anyone inspecting the website.
Instead:
Browser
↓
Our API
↓
Instagram credentials
↓
Instagram API
The browser only communicates with our backend.
INSTAGRAM
│
▼
Meta / Instagram API
│
▼
┌────────────────────────┐
│ Instagram Feed API │
│ │
│ Node.js + Express.js │
│ │
│ Routes │
│ Services │
│ Configuration │
└────────────┬───────────┘
│
JSON
│
▼
GET /api/posts
│
▼
Photographer Website
│
HTML / CSS / JS
The website should not need to understand Meta's API.
It only needs to understand our API.
GET /api/posts[
{
"id": "123",
"caption": "Wedding photography session",
"mediaUrl": "https://...",
"permalink": "https://instagram.com/p/...",
"mediaType": "IMAGE",
"timestamp": "2026-09-07T12:00:00"
},
{
"id": "124",
"caption": "Portrait session",
"mediaUrl": "https://...",
"permalink": "https://instagram.com/p/...",
"mediaType": "IMAGE",
"timestamp": "2026-09-06T15:30:00"
}
]This separation is important.
Instagram's format
↓
Our backend
↓
Our standard format
↓
Website
If Instagram changes something internally, we can update our backend without necessarily changing every website.
The website can contain:
<section id="instagram-feed"></section>JavaScript can then request the feed:
fetch("https://our-api.com/api/posts")
.then(response => response.json())
.then(posts => {
const feed = document.getElementById("instagram-feed");
posts.forEach(post => {
const image = document.createElement("img");
image.src = post.mediaUrl;
image.alt = post.caption;
feed.appendChild(image);
});
});Eventually this will create the photographer's Instagram gallery automatically.
Our initial repository structure:
instagram-feed-api/
│
├── src/
│ ├── routes/
│ │ └── instagramRoutes.js
│ │
│ ├── services/
│ │ └── instagramService.js
│ │
│ └── app.js
│
├── .env
├── .env.example
├── .gitignore
├── package.json
├── package-lock.json
└── README.md
Responsible for:
- starting Express
- middleware
- CORS
- registering routes
- starting the HTTP server
Responsible for endpoints such as:
GET /api/postsResponsible for communicating with Instagram.
We want:
Route
↓
Service
↓
Instagram API
rather than putting all the Instagram logic inside our route.
Secrets will be stored in:
.env
Example:
INSTAGRAM_ACCESS_TOKEN=secret
INSTAGRAM_ACCOUNT_ID=123456
PORT=3000
.env must never be committed to GitHub.
Our .gitignore should contain:
node_modules/
.env
We can safely commit:
.env.example
containing:
INSTAGRAM_ACCESS_TOKEN=
INSTAGRAM_ACCOUNT_ID=
PORT=3000
This shows developers what configuration is required without exposing real credentials.
We will build the project incrementally.
PHASE 1
Node/Express API
↓
PHASE 2
Fake Instagram posts
↓
PHASE 3
Connect website
↓
PHASE 4
Connect Instagram
↓
PHASE 5
Real Instagram posts
↓
PHASE 6
Security + error handling
↓
PHASE 7
Deployment
We don't need to solve everything at once.
Our first goal is:
One authorised Instagram account → one working API → one working website.
The MVP does NOT require:
- multiple clients
- user accounts
- admin dashboard
- payments
- advanced analytics
- complicated database architecture
Those can come later.
Repository
instagram-feed-api
Tasks
- Create GitHub repository.
- Add both developers as collaborators.
- Clone repository.
- Create README.
- Create
.gitignore. - Create initial development branches.
Done when:
Both Mbuyiselo and Busi can clone, branch, commit and push.
Run:
npm init -yInstall:
npm install express cors dotenvDone when:
npm startstarts the application successfully.
Create:
src/app.js
Implement:
GET /Expected response:
{
"message": "Instagram Feed API is running"
}Implement:
GET /api/postsInitially use fake data.
[
{
"id": "1",
"caption": "Photography session",
"mediaUrl": "https://example.com/image.jpg",
"permalink": "https://instagram.com/",
"mediaType": "IMAGE"
}
]Done when:
GET /api/posts returns:
HTTP 200
with valid JSON.
Create:
src/routes/instagramRoutes.js
Move /api/posts routing into this module.
Create:
src/services/instagramService.js
Architecture:
instagramRoutes
↓
instagramService
↓
Instagram API
The endpoint must continue working after the refactor.
Use:
fetch(...)to call:
GET /api/postsDone when:
Fake posts from our backend successfully appear on an HTML page.
Display:
- image
- caption
- post date
- Instagram link
Clicking the post should allow the visitor to open the original Instagram post.
Configure authorised access to the client's Instagram account.
Tasks include:
- Meta developer configuration
- Instagram configuration
- client account authorisation
- obtaining required credentials
- storing secrets in
.env
No credentials may be committed to GitHub.
Update:
instagramService.js
to retrieve actual Instagram media.
Required information includes:
id
caption
media URL
media type
permalink
timestamp
Convert Instagram's response into our own standard format:
{
"id": "...",
"caption": "...",
"mediaUrl": "...",
"permalink": "...",
"mediaType": "...",
"timestamp": "..."
}Architecture:
Instagram
↓
Instagram response
↓
instagramService
↓
Our format
↓
GET /api/posts
↓
Website
Handle:
- Instagram unavailable
- network errors
- expired/invalid credentials
- no posts
- unexpected API responses
The server should not crash.
Example:
{
"error": "Unable to retrieve Instagram posts"
}Return appropriate HTTP status codes.
Verify:
.envignored by Git- tokens aren't logged
- tokens aren't returned to frontend
- tokens aren't committed
- secrets remain server-side
During development:
localhost
For production, restrict access to the appropriate website domain.
We should avoid developing directly on main.
Example:
main
│
├── feature/setup-express
│
├── feature/instagram-route
│
├── feature/instagram-service
│
└── feature/frontend-integration
Workflow:
git checkout main
git pull
git checkout -b feature/instagram-route
# make changes
git add .
git commit -m "feat: add Instagram posts endpoint"
git push -u origin feature/instagram-routeThen:
Push
↓
Pull Request
↓
Code Review
↓
Merge
Mbuyiselo and Busi should review each other's pull requests.
Rather than completely separating the project, both developers should understand the complete architecture.
Initial areas can be divided roughly as:
- Express setup
- REST API
- Instagram service
- Meta integration
- API consumption
- feed UI
- loading/error states
- frontend integration
- API testing
However, we should review and discuss each other's code.
The Instagram/Meta integration should preferably be done together because it is the main external dependency.
Once the MVP works, we can introduce caching.
Instagram
↑
Periodic refresh
│
Instagram Feed API
│
Cache
│
┌───┼────┐
↓ ↓ ↓
Website visitors
This prevents every website visitor from causing another request to Instagram.
The API should eventually be reusable.
Instagram Feed API
│
┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
Photographer A Client B Client C
│ │ │
▼ ▼ ▼
Website A Website B Website C
Future endpoints could look like:
GET /api/feeds/photographer-a/posts
GET /api/feeds/client-b/posts
GET /api/feeds/client-c/postsBut this comes after the MVP.
Version 1 is complete when:
Photographer uploads a post
↓
Instagram
↓
Instagram/Meta API
↓
Our backend API
↓
GET /api/posts
↓
HTML/JS website
↓
Latest post appears on website
At that point we have successfully created the first version of our Instagram Feed API.
ONE INSTAGRAM ACCOUNT
↓
ONE WORKING API
↓
ONE WEBSITE
Multiple clients
↓
Caching/database
↓
Reusable widget
↓
Client authentication
↓
Dashboard
↓
Cloud deployment/scaling
The first objective is not to recreate all of Elfsight.
The objective is to understand and successfully build the core Instagram-feed functionality ourselves.
Once that works, we can gradually evolve it into a reusable service for other websites.