Search by

earlyforge / earlyforge-sdk

beker939

PHP client for the EarlyForge Trends API (free Pulse mirror and paid full trends feed)

v0.1.1 2026-11-08 12:54 UTC

This package is auto-updated.

Last update: 2026-11-08 12:59:10 UTC


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