Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Instagram Parser Laravel Package

mineur/instagram-parser

A PHP package for scraping public Instagram data without the official API. Retrieve profile details, posts, media, and comments through a simple, Laravel-friendly interface—ideal for building feeds, analytics, and lightweight Instagram integrations.

View on GitHub
Deep Wiki
Context7

Getting Started

mineur/instagram-parser is a plain PHP library, not a Laravel package — no service provider, no config file, no facade. Install it and you're working directly with the parser class.

composer require mineur/instagram-parser

It pulls in Guzzle for the HTTP work, so the first thing to verify after install is that Composer didn't downgrade or conflict with the Guzzle version Laravel's Http client and other packages expect. Run composer why guzzlehttp/guzzle and check the resolved version before you commit the lockfile.

The mental model is: give it a public username, it fetches the profile page (or the internal JSON endpoint depending on version), extracts Instagram's embedded shared-data blob, and hands you back the media entries. No app registration, no access token, no OAuth dance — which is exactly why you'd reach for it.

First use case, for a marketing site footer or homepage grid:

use Mineur\InstagramParser\InstagramParser;

$posts = (new InstagramParser())->parse('laravel');

Method names have shifted between releases of this package, so open src/InstagramParser.php in vendor/ before writing anything — read the public method signatures and whatever model/collection class it returns (array vs. objects varies too). Don't trust blog posts or the README alone.

Where to look first, in order:

  1. vendor/mineur/instagram-parser/src/ — the parser class and any Model classes.
  2. The package's tests/fixtures, if present, to see the raw shape it expects.
  3. composer.json of the package for the PHP constraint — older releases will not install on PHP 8.2+/Laravel 11 without --ignore-platform-reqs hacks you should avoid.

Implementation Patterns

Wrap it, don't sprinkle it. The library's API is unstable (scrapers always are). Put it behind one small class so a future replacement is a one-file change:

namespace App\Services;

use Mineur\InstagramParser\InstagramParser;

class InstagramFeed
{
    public function latest(string $username, int $limit = 12): array
    {
        try {
            $media = (new InstagramParser())->parse($username);
        } catch (\Throwable $e) {
            report($e);
            return [];
        }

        return array_slice(is_array($media) ? $media : $media->all(), 0, $limit);
    }
}

Bind it in a service provider if you want to inject it, but a new inside a service class is fine here — there's nothing to configure.

Cache hard, serve from cache. Every render should not be an outbound scrape. Instagram throttles aggressively and will serve you a login wall.

$posts = Cache::remember("instagram:{$username}", now()->addHours(2), fn () =>
    app(\App\Services\InstagramFeed::class)->latest($username)
);

Warm the cache on a schedule, then let request-time code read only the cache and never touch the network:

// routes/console.php
Schedule::command('instagram:refresh laravel')->hourly()->withoutOverlapping();

This is the single most important pattern. It decouples "Instagram is slow/blocking/failing" from "the homepage is slow/broken."

Re-host the images. Instagram's CDN URLs carry signed, expiring query parameters. Store the binary in storage/app/public/instagram/ via a queued job and store the local path in the cache payload. Hotlinking works for a day or two and then produces broken images in your footer, which looks worse than no feed at all.

Always render a fallback. Treat an empty result as a normal state, not an error:

@forelse($posts as $post)
    <img src="{{ $post['local_path'] ?? $post['display_src'] }}" alt="">
@empty
    {{-- static editorial image, or nothing --}}
@endforelse

Map to a DTO before it hits Blade. Raw parsed payloads have inconsistent keys across media types (image, video, carousel). Normalize once into ['image' => ..., 'caption' => ..., 'link' => ..., 'type' => ...] and keep your views boring.

Gotchas and Tips

It's a scraper, and scrapers rot. Instagram has changed its markup and added login walls repeatedly. Expect the parser to silently return an empty array rather than throw when the page shape changes — the extraction step just finds nothing. Always assert on emptiness in your refresh command and log loudly, or you'll discover the breakage from a client instead.

Rate limiting is invisible until it isn't. You'll get HTTP 429s or a redirect to /accounts/login/. Symptoms: everything worked in dev, production started returning nothing three days later. Mitigations that actually help: hourly (not per-request) fetching, a realistic User-Agent, and — if you're on a shared/cloud IP — a proxy. Don't build a retry storm; back off and keep serving the stale cache.

Only public profiles. Private accounts return nothing useful, and there's no credential/2FA path. If the feed you need is private, this package is the wrong tool and you want the official Graph API.

Cache the parsed result, not the media objects. The returned models may hold references to internal state or be awkward to serialize. Convert to arrays/DTOs before Cache::put.

Signed URLs expire. Covered above, but worth repeating as a debugging trap: the feed looks fine in your browser tab (fresh URL) and broken in the cached HTML (URL minted 6 hours ago). Same for video src values.

No pagination in the ergonomic sense. You generally get "the first page" of the profile. If you need 50 posts, you're either increasing the scrape payload or accepting that you can't. Design the UI around "latest N," not "browse the archive."

PHP/Guzzle version friction on modern Laravel. The package's constraints predate Laravel 10/11 defaults. If Composer refuses, don't --ignore-platform-reqs your way past it on a production app — you'll get subtle Guzzle incompatibilities. Either accept an older stack or fork the package and bump the constraint in composer.json.

Extension points are thin. If the parser constructs its own HTTP client internally, you can't easily inject a proxy or a mock. For testability, mock at your wrapper boundary (Http::fake() won't help if it uses Guzzle directly — bind a fake InstagramFeed in the container instead). If it does accept a client, pass a configured Guzzle instance with timeouts set; the default timeout will eventually hang a web request when you forget to use the cache.

ToS and legal. Scraping public profile data is a gray area and Instagram's terms prohibit it. Get a sign-off if this is client work, and keep the scrape volume low enough that nobody notices. The official Graph API is the defensible path when the feed is a product requirement rather than a nice-to-have.

Have an exit plan. With 128 stars, no dependents, and low maintenance activity, treat this as a stopgap. Your wrapper class from the Implementation Patterns section is the exit plan — when the parser finally breaks for good, swap the body of latest() for a Graph API call and nothing else in the app changes.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle