# @socialhose/api TypeScript SDK for the Socialhose Public API. ## Install ```bash npm install @socialhose/api ``` Node 18+ is required because the SDK uses the built-in `fetch`, `Response`, and `AbortSignal.timeout` APIs. You can pass a custom `fetch` implementation if needed. ## Quickstart ```ts import { SocialhoseClient } from '@socialhose/api'; const socialhose = new SocialhoseClient({ apiKey: process.env.SOCIALHOSE_API_KEY!, }); const mentions = await socialhose.getMentions({ content_search: 'hospital', platforms: 'twitter', ordering: '-published_at', }); console.log(mentions.count, mentions.results[0]?.content); ``` ## Configuration ```ts const socialhose = new SocialhoseClient({ apiKey: process.env.SOCIALHOSE_API_KEY!, baseUrl: 'https://socialhose.net/api/public/v1', timeoutMs: 8_000, retries: 3, cacheTtlMs: 60_000, }); ``` The SDK sends `Authorization: Api-Key ` and a browser-like `User-Agent` by default. The user-agent is intentional: the current Socialhose API edge rejects some non-browser requests. ## Endpoints ### Campaigns - `getCampaigns()` - `getCampaign(id)` ### Analytics - `getOverview(filters)` - `getTimeline(filters)` - `getSentiment(filters)` - `getShareOfVoice(filters)` - `getPlatforms(filters)` - `getTopKeywords(filters)` - `getTrending(filters)` - `getTopMentions(filters)` ### Mentions - `getMentions(filters)` ### Mailing Lists - `getMailingLists()` - `inviteMailingListMember(listId, invite)` ### Entity Analytics - `getEntityBrief(term, campaignId?)` — one request: count + top-20 engagement sample with derived sentiment/platform - `getEntityStats(term, campaignId?)` — full dashboard: exact sentiment faceting, exact platform mix, 14-day cumulative-differenced timeline, 7d momentum - `getEntityBriefs(terms, campaignId?, concurrency?)` — batch entity resolution with bounded concurrency (default 20) - `getCampaignIdByMatch(substring)` — resolve a live campaign ID by matching its name ### Low-Level - `get(path, params)` for direct GET access - `post(path, body)` for direct POST access ## Filtering examples ```ts await socialhose.getOverview({ campaign_ids: 'campaign-id', date_from: '2026-05-01', date_to: '2026-05-29', platforms: 'twitter,reddit', sentiments: 'negative', }); await socialhose.getTimeline({ campaign_ids: 'campaign-id', interval: 'day', }); ``` ## Custom Caching The SDK ships with `MemoryCache` (in-memory, per-entry TTL) and `NoopCache` (no caching). You can inject your own by implementing the `Cache` interface: ```ts import { SocialhoseClient, Cache } from '@socialhose/api'; class RedisCache implements Cache { async get(key: string) { /* redis.get(key) */ } async set(key: string, value: unknown, ttlMs: number) { /* redis.set(key, value, 'PX', ttlMs) */ } async delete(key: string) { /* redis.del(key) */ } } const socialhose = new SocialhoseClient({ apiKey: process.env.SOCIALHOSE_API_KEY!, cache: new RedisCache(), }); ``` Pass `revalidateSeconds` per request to control per-call TTL in your cache implementation: ```ts await socialhose.getMentions( { content_search: 'ozempic' }, { revalidateSeconds: 3600 }, ); ``` ## Entity Analytics Search across all mentions for a specific term, person, or organization: ```ts // Quick count + top mentions const brief = await socialhose.getEntityBrief('Burhan'); console.log(brief.total, brief.sentiment, brief.platformMix); // Full dashboard: exact distributions, timeline, momentum const stats = await socialhose.getEntityStats('RSF', 'campaign-id'); console.log( stats.total, stats.momentumPct, // last 7 days vs prior 7 stats.sentiment, // exact (facets reconcile) or estimated (from sample) stats.sparkline, // 14-day daily volume ); // Batch resolve many entities with bounded concurrency const briefs = await socialhose.getEntityBriefs( ['Burhan', 'Hemedti', 'SAF', 'RSF'], 'campaign-id', 10, // concurrency ); ``` Entity analytics fan out multiple requests per entity (sentiment faceting: 3 calls; platform mix: 6 calls; timeline: 15 calls). Set `cacheTtlMs` or inject a persistent cache to stay under the ~60 req/min rate limit. ## Pitfalls **Cloudflare UA blocking.** The Socialhose API sits behind Cloudflare, which rejects some non-browser User-Agent headers. The SDK defaults to a Chrome 124 UA — don't change it unless you've verified the new UA works. **Entity timeline uses cumulative differencing.** The analytics timeline endpoint is campaign-scoped and ignores `content_search`. The SDK facets `/mentions/` by day using cumulative `date_from`-only queries and subtracts consecutive counts. This avoids the API's `date_to` inclusivity bug: overlapping `[date_from, date_to]` windows share a day and double-count it. Don't "simplify" this to day windows. **Sentiment reconciliation checks.** The `getEntityStats` exact sentiment and platform distributions validate that facet counts reconcile with the known total (e.g., positive + negative + neutral === total). If they don't match, the API silently dropped the `content_search` filter — the SDK falls back to estimates from the brief's sample rather than showing wrong data. **Rate limit ~60 req/min per API key.** Entity analytics fan out many parallel requests. Use the `cache` option with a persistent store (Redis, Next.js Data Cache) to keep warm loads under the limit. **Never expose the API key to the browser.** This SDK is designed for server-side use. Always set `SOCIALHOSE_API_KEY` as a server-side environment variable. ## Errors Failed requests throw `SocialhoseError` with `status`, `path`, and response `body` when available. ```ts import { SocialhoseError } from '@socialhose/api'; try { await socialhose.getCampaign('missing'); } catch (error) { if (error instanceof SocialhoseError) { console.error(error.status, error.path, error.body); } } ``` ## Publishing ```bash pnpm test pnpm typecheck pnpm build npm publish --access public --provenance ```