earlyforge / earlyforge-sdk
PHP client for the EarlyForge Trends API (free Pulse mirror and paid full trends feed)
Package info
github.com/Earlyforge-official/earlyforge-sdk-php
pkg:composer/earlyforge/earlyforge-sdk
Requires
- php: >=8.1
- ext-curl: *
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
PHP client for the EarlyForge Trends API, with two tiers under the
same /v1/api/trends/* prefix:
- Pulse mirror — free on every account, no plan required. An authenticated mirror of the free, anonymous trend data already shown on pulse.earlyforge.ai: same daily snapshot, same trend count, same fields.
- Full trends feed — every plan, including free, and not "all
trends unlocked." Every plan reveals a daily, random,
category-diversified budget of trend names (bigger on paid plans) —
exactly what your organization's dashboard Trends page shows unlocked
today, no more, no less. See
fullTrends()below.
This is not a client for articles, briefs, or alerts — the public API
never exposes those on any plan. Zero dependencies: uses ext-curl
directly rather than pulling in an HTTP client library.
Full reference: docs.earlyforge.ai/guides/api/php-sdk. Source: github.com/Earlyforge-official/earlyforge.
Install
Not published to Packagist yet. Contact support@earlyforge.ai for early access.
Quickstart
use EarlyForge\Pulse\PulseClient; $client = new PulseClient('ef_live_xxxxxxxxxxxxxxxx'); $snapshot = $client->topTrends(); echo $snapshot['trends'][0]['display_name'];
Get a key from Settings → API Keys in the EarlyForge console — any account works, Pulse access is free.
Methods
topTrends(?string $date = null): array
Global top trends — same snapshot as the Pulse homepage. Returns
['trends' => [...], 'total' => int, 'cached_at' => string, 'cache_ttl_seconds' => int].
trendsByRegion(string $region, ?string $date = null): array
Top trends for an ISO 3166-1 alpha-2 region code (e.g. "US", "FR").
Same shape as topTrends().
trendsByCategory(string $category, ?string $date = null): array
Top trends for a category (e.g. "politics", "tech"). Same shape as
topTrends().
getTrend(string $trendId): array
Detail for one trend. Same fields as a trend, plus canonical_name,
sources, a full description, and a timeline of {ts, score, sources_count} points.
fullTrends(?string $region = null, ?string $category = null, int $page = 1, int $limit = 20): array
Every plan, including free. A bigger, paginated slice of the trend corpus
than the fixed Pulse snapshot — gated by the same daily random
name-visibility budget as the dashboard (bigger budget on paid plans).
Items outside today's budget stay in the list with their name fields
redacted ('name_locked' => true), not removed.
Returns ['items' => [...], 'total' => int, 'page' => int, 'per_page' => int].
getFullTrend(string $trendId): array
Every plan, including free. Full detail for one trend — same TD-sourced fields
as the dashboard's detail panel (topsis_score, editorial_title, etc.).
Throws PulseApiException with getStatusCode() === 402 and
getDetail()['code'] === 'trend_locked' if the trend is outside today's
visible-name budget.
Errors
Every non-2xx response throws EarlyForge\Pulse\PulseApiException with
getStatusCode() and getDetail() (mirroring the OpenAPI spec's 401
missing/invalid/revoked key, 404 unknown trend, and 502 upstream
unavailable responses). getDetail() is a plain string on the Pulse
endpoints, but a structured array (['error' => ..., 'code' => ...]) on
the full-trends 402s — check is_array() before using it. Network/cURL
failures also throw PulseApiException.
use EarlyForge\Pulse\PulseApiException; try { $client->getTrend('does-not-exist'); } catch (PulseApiException $e) { echo $e->getStatusCode() . ': ' . $e->getDetail(); }
Rate limits
No monthly call quota on this surface, for any plan — only the shared 60 requests/60s burst limit applies, same as the anonymous Pulse page.
Tests
composer install
composer test