PHPackages                             rastographer/igdownloader - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. rastographer/igdownloader

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

rastographer/igdownloader
=========================

Instagram media extraction and signed proxy delivery for Laravel applications.

0.1.6(3w ago)06MITPHPPHP ^8.3

Since Mar 13Pushed 3w agoCompare

[ Source](https://github.com/rastographer/igdownloader)[ Packagist](https://packagist.org/packages/rastographer/igdownloader)[ RSS](/packages/rastographer-igdownloader/feed)WikiDiscussions main Synced 3w ago

READMEChangelog (5)Dependencies (10)Versions (6)Used By (0)

Laravel IG Downloader
=====================

[](#laravel-ig-downloader)

`rastographer/igdownloader` is a Laravel package for extracting Instagram media metadata and serving that media through signed Laravel routes.

The package currently provides:

- URL parsing for Instagram post, reel, TV, and reels URLs.
- URL parsing for Instagram stories, story highlights, and profile URLs.
- A strategy-based extraction pipeline.
- Signed preview and download routes.
- Upstream HTTP fetching with optional proxy resolution.
- A reusable service contract for host applications.

The package does not currently provide:

- A frontend UI.
- Database migrations.
- A finished GraphQL extraction strategy.

Compatibility
-------------

[](#compatibility)

This package currently supports:

- PHP `^8.3`
- Laravel `^13.0`

This is based on the package `composer.json`. The package is not declared compatible with Laravel 10, Laravel 11, or Laravel 12.

Package Status
--------------

[](#package-status)

Current implementation status:

- `StoryStrategy` is implemented for story and highlight URLs.
- `ProfileStrategy` is implemented for profile-picture URLs.
- `CanonicalStrategy` is implemented.
- `EmbedStrategy` is implemented.
- `GraphQLStrategy` is a placeholder and returns `null`.
- Route auto-discovery is enabled through the package service provider.

Prerequisites
-------------

[](#prerequisites)

Before installing the package into any Laravel application, confirm the following:

1. The application runs on PHP `8.3` or newer.
2. The application runs on Laravel `13`.
3. The application can make outbound HTTPS requests to Instagram and the media CDN hosts.
4. The application has a working cache store.
5. The application can generate signed URLs.
6. The application has a writable storage path for normal Laravel runtime operations.

Additional prerequisites when proxy rotation is required:

1. You must provide a `ProxyResolver` implementation.
2. If that resolver reads from the database, the host application must own the schema and migrations for that proxy storage.

Installation
------------

[](#installation)

### Option 1: Local path repository

[](#option-1-local-path-repository)

This is the current installation method used in this repository.

Add a path repository to the host application's `composer.json`:

```
{
  "repositories": [
    {
      "type": "path",
      "url": "packages/rastographer/igdownloader",
      "options": {
        "symlink": true
      }
    }
  ],
  "require": {
    "rastographer/igdownloader": "^0.1.3"
  }
}
```

Then install it:

```
composer update rastographer/igdownloader
php artisan package:discover --ansi
```

### Option 2: Standard Composer install

[](#option-2-standard-composer-install)

Use this after the package is moved to a VCS repository or Packagist.

```
composer require rastographer/igdownloader
php artisan package:discover --ansi
```

Service Provider Registration
-----------------------------

[](#service-provider-registration)

The package uses Laravel package auto-discovery.

You do not need to manually register `Rastographer\IgDownloader\IgDownloaderServiceProvider` if Composer package discovery is working.

Publishing Configuration
------------------------

[](#publishing-configuration)

Publish the package config with:

```
php artisan vendor:publish --tag=igdownloader-config
```

This publishes the config to:

```
config/igdownloader.php

```

Configuration Reference
-----------------------

[](#configuration-reference)

Default configuration lives in `config/igdownloader.php`.

### `routes`

[](#routes)

- `enabled`: Controls whether the package registers routes automatically.
- `prefix`: URL prefix for the package routes. Default: empty string.
- `name`: Route name prefix. Default: `igdownloader.`
- `middleware`: Middleware stack for package routes. Default: `['web']`
- `throttle`: Throttle middleware applied to the fetch route. Default: `throttle:10,1`

Important: with the current default `prefix` of `''`, the package registers these paths directly at the application root:

- `POST /fetch`
- `GET /img`
- `GET /dl`

If the host application already uses those paths, set a prefix such as `ig`.

### `cache`

[](#cache)

- `ttl`: Extraction result cache TTL in seconds.
- `signed_url_ttl_minutes`: TTL for signed preview and download URLs.
- `source_ttl`: TTL for stored source URL keys used by preview routes.

### `http`

[](#http)

- `user_agent`: User agent sent to upstream requests.
- `connect_timeout`: HTTP connection timeout in seconds.
- `timeout`: Total HTTP request timeout in seconds.
- `verify`: Whether TLS certificate verification is enabled.

### `security`

[](#security)

- `allowed_hosts`: List of hosts and base domains that are allowed for preview/download proxying.

This is a mandatory security boundary. Only URLs matching these hosts are allowed to be streamed through the package routes.

### `logging`

[](#logging)

- `channel`: Laravel log channel used by the package.

Environment variable:

```
IGDL_LOG_CHANNEL=instagram
```

### `proxy`

[](#proxy)

- `enabled`: Enables proxy resolution.
- `resolver`: Fully-qualified class name implementing `Rastographer\IgDownloader\Contracts\ProxyResolver`
- `pool`: Static config-based proxy pool used when no custom resolver class is provided

### `strategies`

[](#strategies)

- `order`: Ordered list of extraction strategies to attempt.
- `map`: Strategy key to class mapping.

Current default order:

```
['story', 'profile', 'canonical', 'embed', 'graphql']
```

When a consuming application has an older published `igdownloader` config, the service provider appends any missing built-in strategies automatically so story/profile support can still resolve without a config crash.

Environment Variables
---------------------

[](#environment-variables)

The current host config supports these environment variables:

```
IG_USER_AGENT="Mozilla/5.0"
IG_ALLOWED_HOSTS="cdninstagram.com,fbcdn.net"
IG_DOWNLOAD_TTL=10
IG_CACHE_TTL=86400
IG_PROXY_ENABLED=false
IG_STRATEGY_ORDER=embed,canonical,graphql
IGDL_LOG_CHANNEL=instagram
```

Registered Routes
-----------------

[](#registered-routes)

When routes are enabled, the package registers:

- `POST /fetch` as `igdownloader.fetch`
- `GET /img` as `igdownloader.preview`
- `GET /dl` as `igdownloader.download`

If `routes.prefix` is set to `ig`, the effective URLs become:

- `POST /ig/fetch`
- `GET /ig/img`
- `GET /ig/dl`

Route Contracts
---------------

[](#route-contracts)

### `POST igdownloader.fetch`

[](#post-igdownloaderfetch)

Expected request payload:

```
{
  "url": "https://www.instagram.com/p/XXXXXXXXXXX/",
  "expect": "any"
}
```

Validation rules:

- `url`: required, must be a valid URL.
- `expect`: optional, must be one of `image`, `video`, or `any`.

Supported URL categories now include:

- Post URLs such as `https://www.instagram.com/p/.../`
- Reel URLs such as `https://www.instagram.com/reel/.../` and `https://www.instagram.com/reels/.../`
- TV URLs such as `https://www.instagram.com/tv/.../`
- Story URLs such as `https://www.instagram.com/stories/{username}/{storyId}/`
- Story highlight URLs such as `https://www.instagram.com/stories/highlights/{highlightId}/`
- Profile URLs such as `https://www.instagram.com/{username}/`

Current successful response shape:

```
{
  "ok": true,
  "shortcode": "ABC123XYZ90",
  "cover": "https://your-app.test/img?...",
  "items": [
    {
      "position": 1,
      "kind": "video",
      "preview": "https://your-app.test/img?...",
      "download": "https://your-app.test/dl?...",
      "url": "https://cdninstagram.com/...",
      "size": null,
      "quality": "1080p"
    }
  ],
  "schema_version": 2
}
```

Current error response shape:

```
{
  "ok": false,
  "error": {
    "code": "INVALID_URL",
    "title": "Invalid link",
    "message": "That does not look like a valid Instagram link.",
    "help": "Example: https://www.instagram.com/p/XXXXXXXXXX/"
  }
}
```

Possible error codes currently returned by the package controller:

- `INVALID_URL`
- `NO_MEDIA`
- `RATE_LIMIT`
- `NETWORK`

### `GET igdownloader.preview`

[](#get-igdownloaderpreview)

This route streams an image-like preview response inline.

Requirements:

- The route must be signed.
- The `key` query parameter must map to a stored source URL.
- The resolved source URL must pass `allowed_hosts`.

### `GET igdownloader.download`

[](#get-igdownloaderdownload)

This route streams a file download response.

Requirements:

- The route must be signed.
- `u` must be a base64 encoded upstream URL.
- `h` must equal `sha256(u-decoded)`.
- The decoded source URL must pass `allowed_hosts`.

Programmatic Usage
------------------

[](#programmatic-usage)

### Parse a URL

[](#parse-a-url)

```
use Rastographer\IgDownloader\Contracts\Downloader;

$parsed = app(Downloader::class)->parseUrl('https://www.instagram.com/reel/ABC123XYZ90/');
```

Returned shape:

```
[
    'shortcode' => 'ABC123XYZ90',
    'kind' => 'reel',
]
```

### Fetch media by shortcode

[](#fetch-media-by-shortcode)

```
use Rastographer\IgDownloader\Contracts\Downloader;
use Rastographer\IgDownloader\DTO\ExtractionContext;

$result = app(Downloader::class)->fetch(
    'ABC123XYZ90',
    new ExtractionContext(preferredKind: 'reel', requestId: 'req-123')
);
```

`Downloader::fetch()` remains the shortcode-oriented contract. For story, highlight, and profile URLs, prefer the URL-based action below.

### Use the package action

[](#use-the-package-action)

```
use Rastographer\IgDownloader\Services\FetchMediaAction;

$payload = app(FetchMediaAction::class)->handle(
    'https://www.instagram.com/stories/instagramuser/xxxxxxxxxxxxxxx/',
    'req-123'
);
```

Proxy Integration
-----------------

[](#proxy-integration)

The package supports two proxy models:

1. Static config pool using `proxy.pool`
2. Custom resolver via `proxy.resolver`

### Custom resolver contract

[](#custom-resolver-contract)

Implement:

```
Rastographer\IgDownloader\Contracts\ProxyResolver
```

Contract methods:

- `resolve(): ?ProxyDefinition`
- `reportSuccess(?ProxyDefinition $proxy): void`
- `reportFailure(?ProxyDefinition $proxy, ?string $reason = null): void`

### Example: database-backed resolver

[](#example-database-backed-resolver)

This repository currently provides one at `app/IgDownloader/DatabaseProxyResolver.php`.

It selects enabled proxies from the host application's `proxies` table and updates:

- `success_count`
- `fail_count`
- `status`
- `last_checked_at`

Important: the package does not ship the `proxies` table migration. That schema belongs to the host application.

Security Model
--------------

[](#security-model)

The package is designed to avoid exposing raw upstream URLs directly to the frontend.

Security controls currently implemented:

- Signed preview route
- Signed download route
- Short-lived signed URL TTL
- Upstream host allowlist enforcement
- URL integrity hash on download requests

You should not disable `allowed_hosts` checking in production.

Logging
-------

[](#logging-1)

The package logs extraction attempts, hits, misses, exceptions, and fetch success summaries through the configured Laravel log channel.

If your application does not define a dedicated `instagram` channel, point `IGDL_LOG_CHANNEL` to an existing channel such as `stack` or `stderr`.

Testing
-------

[](#testing)

The package includes tests under `packages/rastographer/igdownloader/tests`.

If you are running the package tests against a consuming Laravel application bootstrap, point the package test harness at that application:

```
IGDL_TEST_BASE_PATH=/path/to/your-app php /path/to/your-app/vendor/bin/pest packages/rastographer/igdownloader/tests --compact
```

In this workspace, the tests were run against the `snapigdownloader` host app bootstrap.

Updating the Package
--------------------

[](#updating-the-package)

### If installed as a local path repository

[](#if-installed-as-a-local-path-repository)

The host application uses the working tree directly. Update process:

1. Change the package code under `packages/rastographer/igdownloader`
2. Run:

```
composer update rastographer/igdownloader --no-scripts
php artisan package:discover --ansi
```

3. Re-run the relevant tests.

### If installed from VCS or Packagist

[](#if-installed-from-vcs-or-packagist)

```
composer update rastographer/igdownloader
php artisan package:discover --ansi
```

If the package introduces new config keys, republish or manually merge config changes:

```
php artisan vendor:publish --tag=igdownloader-config --force
```

Use `--force` carefully because it overwrites the published config file.

Releasing a New Version
-----------------------

[](#releasing-a-new-version)

Recommended release workflow:

1. Update package code.
2. Update package tests.
3. Update version in the package `composer.json`.
4. Tag the release in the package repository.
5. Update consuming applications to the new version constraint.

Current Limitations
-------------------

[](#current-limitations)

These are current, precise limitations of the package as implemented today:

- Laravel 13 only.
- PHP 8.3+ only.
- No UI components or pages.
- No package migrations.
- No built-in database proxy storage.
- `GraphQLStrategy` is not implemented.
- Story, highlight, and profile extraction still depend on public Instagram markup remaining parseable.
- Public-story and highlight availability still depends on what Instagram exposes without authentication.

Recommended Production Requirements
-----------------------------------

[](#recommended-production-requirements)

For production use, the consuming application should provide:

1. A stable cache backend such as Redis or Memcached.
2. A dedicated log channel.
3. A proxy resolver if upstream request rotation is required.
4. Monitoring for upstream rate limits and extraction failures.
5. A controlled route prefix if `/fetch`, `/img`, or `/dl` conflict with existing application routes.

File Map
--------

[](#file-map)

Core package entry points:

- `src/IgDownloaderServiceProvider.php`
- `src/Services/DownloaderManager.php`
- `src/Services/FetchMediaAction.php`
- `src/Services/InstagramHtmlMediaExtractor.php`
- `src/Strategies/StoryStrategy.php`
- `src/Strategies/ProfileStrategy.php`
- `src/Strategies/CanonicalStrategy.php`
- `src/Strategies/EmbedStrategy.php`
- `routes/web.php`

Host application integration example:

- `config/igdownloader.php`
- `app/IgDownloader/DatabaseProxyResolver.php`

Thank you!

###  Health Score

39

—

LowBetter than 85% of packages

Maintenance95

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity43

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~19 days

Total

5

Last Release

24d ago

PHP version history (2 changes)0.1.0PHP ^8.2

0.1.6PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/8bbfbd4faf3680f9c9ddafe023fadf0f1a00f56773d59423979d27c6dd0cb161?d=identicon)[rastographer](/maintainers/rastographer)

---

Top Contributors

[![rastographer](https://avatars.githubusercontent.com/u/117541100?v=4)](https://github.com/rastographer "rastographer (17 commits)")

### Embed Badge

![Health badge](/badges/rastographer-igdownloader/health.svg)

```
[![Health](https://phpackages.com/badges/rastographer-igdownloader/health.svg)](https://phpackages.com/packages/rastographer-igdownloader)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.1M337](/packages/psalm-plugin-laravel)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76518.2M119](/packages/laravel-mcp)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k14.1M122](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9742.3M121](/packages/roots-acorn)[spatie/laravel-export

Create a static site bundle from a Laravel app

672139.5k6](/packages/spatie-laravel-export)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
