dantepiazza / apify
Lightweight Apify API client with optional JSON file caching and extensible normalization.
v1.0.0
2026-06-24 22:58 UTC
Requires
- php: ^8.1
- guzzlehttp/guzzle: ^7.0
Requires (Dev)
- phpunit/phpunit: ^10.0
README
Lightweight Apify API client for PHP. No framework dependencies — only Guzzle.
Features:
- Fetch Instagram posts via Apify with a single method call
- JSON file cache enabled by default — identical queries skip the Apify run entirely
- Cache key derived from all query parameters — any change (accounts, limit, extras) triggers a fresh run
- Extensible normalizer — swap
PassthroughNormalizerfor your own field mapping - Cache driver is swappable via
CacheInterface— bring your own Redis, DB, etc.
Installation
composer require dantepiazza/apify
Quick start
use DantePiazza\Apify\ApifyClient; $client = new ApifyClient(token: 'apify_api_xxx'); $posts = $client->fetchInstagramPosts( accounts: ['nasa', 'spacex'], resultsLimit: 50, );
By default the result is cached in sys_get_temp_dir()/dantepiazza_apify/. Calling the same query again returns the cached JSON instantly without hitting Apify.
Cache behaviour
| Scenario | What happens |
|---|---|
| Same accounts + same limit | Returns cached JSON |
| Different accounts | Cache MISS → fresh Apify run |
| Different limit | Cache MISS → fresh Apify run |
Any $extra param changed |
Cache MISS → fresh Apify run |
Custom cache directory & TTL
use DantePiazza\Apify\Cache\FileCache; $client = new ApifyClient( token: 'apify_api_xxx', cache: new FileCache( directory: '/var/cache/apify', ttl: 3600, // seconds; null = never expires (default) ), );
Disable cache globally
$client = new ApifyClient( token: 'apify_api_xxx', cacheEnabled: false, );
Disable cache for a single call
$posts = $client->withoutCache()->fetchInstagramPosts(['nasa']);
Flush all cached entries
$client->flushCache();
Bring your own cache driver
Implement DantePiazza\Apify\Contracts\CacheInterface:
use DantePiazza\Apify\Contracts\CacheInterface; class RedisCache implements CacheInterface { public function get(string $key): ?array { /* ... */ } public function put(string $key, array $data): void { /* ... */ } public function forget(string $key): void { /* ... */ } public function flush(): void { /* ... */ } } $client = new ApifyClient( token: 'apify_api_xxx', cache: new RedisCache($redis), );
Custom normalizer
By default items are returned exactly as Apify delivers them (PassthroughNormalizer).
To apply your own field mapping, implement NormalizerInterface:
use DantePiazza\Apify\Contracts\NormalizerInterface; class InstagramNormalizer implements NormalizerInterface { public function normalize(array $item): ?array { // Return null to discard the item if (empty($item['timestamp'])) { return null; } return [ 'id' => (string) ($item['id'] ?? $item['shortCode'] ?? uniqid()), 'account' => $item['ownerUsername'] ?? '', 'caption' => $item['caption'] ?? '', 'image_url' => $item['displayUrl'] ?? '', 'post_url' => $item['url'] ?? '', 'published_at' => (new DateTimeImmutable($item['timestamp']))->format(DateTimeInterface::ATOM), 'likes' => $item['likesCount'] ?? 0, 'comments' => $item['commentsCount'] ?? 0, ]; } } $client = new ApifyClient( token: 'apify_api_xxx', normalizer: new InstagramNormalizer(), );
Full constructor reference
new ApifyClient( token: string, // required — Apify API token pollTimeout: int = 60, // max polling attempts (×5s = ~5 min) httpTimeout: int = 30, // Guzzle HTTP timeout in seconds cache: ?CacheInterface = null, // null → uses FileCache by default cacheEnabled: bool = true, // set false to disable cache entirely normalizer: ?NormalizerInterface = null, // null → PassthroughNormalizer verifySsl: bool = true, // set false for local dev if needed verbose: bool = false, // echo progress to stdout );
Directory structure
src/
├── ApifyClient.php ← main entry point
├── Cache/
│ └── FileCache.php ← JSON file cache driver
├── Contracts/
│ ├── CacheInterface.php ← cache driver contract
│ └── NormalizerInterface.php ← normalizer contract
├── Exceptions/
│ ├── ApifyException.php ← API / polling errors
│ └── CacheException.php ← cache I/O errors
└── Normalizers/
└── PassthroughNormalizer.php ← default: returns items as-is
License
MIT