Instagram has two APIs, and picking the wrong one costs you a week
Not a wrong endpoint. A wrong API. They have different hosts, different permissions, different tokens and different capabilities, and nothing tells you until something fails in a way that makes no sense.
The two doors
Instagram Login. You send people to Instagram, they approve, and you get a token for graph.instagram.com. The account has to be professional, which is a free switch in settings. You can publish posts and Reels, read comments, reply to them, read insights and receive mentions.
Facebook Login. You send people to Facebook, they approve, they pick a Page, and you get a Page token for graph.facebook.com. The Instagram account has to be linked to a Facebook Page. You get everything above, plus deleting posts, liking, hiding comments, hashtag search, shopping tags, scheduled events and looking up other business accounts.
Same account. Same product, apparently. Wildly different abilities.
The part that actually hurts
You cannot upgrade. Connect through Instagram, then discover you need to hide a comment, and you are not adding a permission. You are disconnecting and starting again. If you are building a product, that means asking a customer to reconnect an account they already connected, which is a support ticket you wrote for yourself.
So the decision belongs at the very top of the integration, before anything else.
How I found out
I had a test asserting Instagram could not delete a post. It passed for weeks. It was wrong. Instagram deletes posts fine on the Facebook path, and my test had encoded a limitation of the door I happened to pick, then presented it as a fact about the platform.
Then a worse one. My "unlike" test passed the entire time, because the code that treats "already unliked" as success was swallowing a genuinely broken endpoint. Likes live on the user node, not the media node. Posting to the media node answers "does not support this operation", which reads like the post is missing rather than like your URL is wrong.
Green tests told me my code did what I told it to. They said nothing about whether I had told it the right thing.
The landmines, in the order they get you
- Images must be JPEG. PNG is rejected outright. This is the one that eats an afternoon.
- Images need a public URL. You cannot upload bytes to the posting endpoint, so you need hosting first.
- Publishing is two calls. Create a container, poll until it is ready, then publish. Carousels create one container per image plus one for the carousel.
- Product tags go on the slides, not the carousel. The wrapper rejects them, and the wrapper is exactly where you would put them, because that is where the caption goes.
- Hashtag search is 30 distinct tags per account per 7 days. A weekly budget rather than a rate limit, so do not search speculatively.
- Hashtag results will not name the author. Instagram refuses the username field there, and asking for it fails the whole search rather than omitting a column.
- An unused hashtag returns 400, not an empty list. Pass that through as an error and your product reports a failure when the honest answer is that nothing matched.
- Profile lookup only works for professional accounts. A personal handle comes back as not found, which looks like a bug and is not.
The error messages are the real difficulty
Meta's errors are frequently accurate and useless at the same time.
One told me "the account owner has disabled access to Instagram Direct messages". That sounds like an app permission problem. It is not. It is a switch inside the Instagram app, under Settings, then Messages and story replies, then Connected tools. No amount of App Review fixes it, and nothing in the message points there.
Another told me an object "does not exist, cannot be loaded due to missing permissions, or does not support this operation". Three unrelated causes in one sentence, about an object that plainly existed, because I had published to it minutes earlier.
The most valuable thing you can do for the people using your software is translate these. Not prettify them. Translate them into the one action that fixes it, and say plainly when that action is one only they can take.
What I would tell myself at the start
Pick the door before writing a line, and pick Facebook Login unless you have a specific reason not to. The setup cost is one extra step for the person connecting, and it buys capabilities you will otherwise need at the worst possible moment.
Then stop trusting empty results. Half the bugs above looked exactly like "there is nothing there".