SDKs
Offizielle Clients für Node.js und Python für die FeedHorizon-API, mit Typen, Wiederholungen, Paginierung und Webhook-Prüfung.
FeedHorizon hat offizielle SDKs für Node.js und Python. Sie sind schlanke Clients für die REST-API und verwenden für jedes Feld dieselben Namen. Jedes Beispiel auf dieser Seite funktioniert genauso wie die passende cURL-Anfrage in der API-Referenz.
Was die SDKs dir abnehmen:
- Typen: Anfragen und Antworten sind typisiert, erzeugt aus /openapi.json.
- Wiederholungen: Netzwerkfehler, Timeouts,
429und5xxwerden mit Backoff wiederholt, unter Beachtung vonRetry-After. - Idempotenz: Jeder Schreibzugriff bekommt einen
Idempotency-Key, bei jedem Versuch denselben, sodass eine Wiederholung nie einen zweiten Post anlegt. - Paginierung: Eine Schleife läuft über alle Seiten einer Liste.
- Fehler: eine Fehlerklasse pro Status, mit
code,param,detailsund der Request-ID. - Webhooks: Signaturprüfung und Parsen in einem Aufruf.
| Paket | Quellcode | Voraussetzung | |
|---|---|---|---|
| Node.js / TypeScript | @feedhorizon/sdk | bash-bash/feedhorizon-node | Node.js 20+, auch Deno, Bun und Edge-Runtimes. Keine Abhängigkeiten. |
| Python | feedhorizon | bash-bash/feedhorizon-python | Python 3.10+, braucht httpx |
Beide stehen unter der MIT-Lizenz.
Installieren
npm install @feedhorizon/sdkClient anlegen
Leg unter Dashboard → API & Apps einen API-Key an und setz ihn als Umgebungsvariable FEEDHORIZON_API_KEY. Der Client liest ihn von dort.
import FeedHorizon from '@feedhorizon/sdk';
const client = new FeedHorizon(); // reads FEEDHORIZON_API_KEYAlternativ übergibst du den Key im Code als apiKey (Node.js) oder api_key (Python). Committe nie einen Key.
Post anlegen
const { data: profiles } = await client.profiles.list();
const post = await client.posts.create({
profileId: profiles[0].id,
content: 'Hello from the FeedHorizon SDK!',
platforms: ['linkedin', 'bluesky'],
scheduledFor: '2026-11-12T09:00:00+01:00',
});
console.log(post.id, post.status); // post_… scheduledMethoden geben das Objekt aus data direkt zurück. Lass das Zeitfeld für einen Entwurf weg, oder schick addToQueue oder publishNow statt scheduledFor (siehe Planen). In Python sind Antworten einfache Dicts mit Type Hints, und Parameter behalten die Namen der API (profileId, nicht profile_id).
Listen und Paginierung
Ein List-Aufruf liefert die erste Seite. Lauf mit einer Schleife darüber, um jedes Element aller Seiten zu bekommen; das SDK lädt die nächste Seite, wenn es sie braucht.
const page = await client.posts.list({ status: 'failed', limit: 50 });
console.log(page.data.length, page.hasNextPage());
for await (const post of client.posts.list({ updatedSince: '2026-11-01T00:00:00Z' })) {
console.log(post.id, post.status);
}from ist in Python ein Schlüsselwort, deshalb heißt der Kalenderfilter von posts.list dort from_.
Fehler
Jeder API-Fehler ist ein APIError mit status, code, message, param, details und der Request-ID. Verzweige anhand von code; Fehler listet alle auf.
import { ConflictError, InvalidRequestError } from '@feedhorizon/sdk';
try {
await client.posts.update(postId, { content: 'New text' });
} catch (error) {
if (error instanceof InvalidRequestError) {
for (const d of error.details) console.log(d.code, d.param, d.message);
} else if (error instanceof ConflictError && error.code === 'post_not_editable') {
// already publishing or published
} else {
throw error;
}
}| Klasse | Status |
|---|---|
InvalidRequestError | 400, 422 |
AuthenticationError | 401 |
PermissionDeniedError | 403 |
NotFoundError | 404 |
ConflictError | 409 |
RateLimitError | 429, mit der Wartezeit in Sekunden als retryAfter / retry_after |
InternalServerError | 5xx |
APIConnectionError, APITimeoutError | Keine Antwort vom Server |
Gib die Request-ID (requestId / request_id) an, wenn du den Support kontaktierst.
Wiederholungen und Idempotenz
Fehlgeschlagene Anfragen werden standardmäßig zweimal wiederholt. Übergib einen eigenen Idempotency-Key, damit ein Schreibzugriff auch über Neustarts des Prozesses hinweg sicher wiederholbar ist, zum Beispiel aus einer Job-Queue:
await client.posts.create(params, { idempotencyKey: `launch-${campaignId}` });
// per client or per request
const patient = new FeedHorizon({ maxRetries: 5, timeout: 30_000 });
await client.posts.get(postId, { maxRetries: 0, timeout: 5_000 });Wie der Server wiederholte Keys behandelt, steht unter Idempotenz.
Medien
// From a public URL
const image = await client.media.import('https://example.com/launch.jpg');
// From a file (up to 5 GB)
import { readFile } from 'node:fs/promises';
const video = await client.media.upload({ file: await readFile('teaser.mp4'), filename: 'teaser.mp4', mimeType: 'video/mp4' });
await client.posts.create({ profileId, content: 'The teaser is out', platforms: ['instagram'], mediaIds: [image.id, video.id], addToQueue: true });upload meldet die Datei an und lädt die Bytes in einem Aufruf hoch. Siehe Medien hochladen.
Webhooks
Prüf die Signatur gegen den unveränderten Request-Body und verarbeite dann das Event. In TypeScript ist event.data passend zu event.event typisiert.
import express from 'express';
import { constructWebhookEvent, WebhookVerificationError } from '@feedhorizon/sdk';
app.post('/webhooks/feedhorizon', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = await constructWebhookEvent({
payload: req.body,
signature: req.get('X-Webhook-Signature'),
secret: process.env.FEEDHORIZON_WEBHOOK_SECRET!,
});
} catch (error) {
if (error instanceof WebhookVerificationError) return res.sendStatus(400);
throw error;
}
res.sendStatus(200);
if (event.event === 'post.target.failed') {
console.log(event.data.platform, event.data.error?.code);
}
});Events, die älter als fünf Minuten sind, werden abgelehnt; das verhindert Replays. Alle Events stehen unter Webhooks.
Async Python
AsyncFeedHorizon hat dieselben Methoden wie FeedHorizon, mit await:
import asyncio
from feedhorizon import AsyncFeedHorizon
async def main() -> None:
async with AsyncFeedHorizon() as client:
post = await client.posts.get("post_…")
async for failed in client.posts.list(status="failed"):
print(failed["id"])
asyncio.run(main())Ressourcen
| Node.js | Python | Methoden |
|---|---|---|
client.me() | client.me() | Organisation, Tarif, Rate Limit und Scopes des Keys |
client.posts | client.posts | list, get, create, validate, update, delete, publish, cancel, retry, duplicate |
client.media | client.media | list, get, create, import (Python: import_url), upload, delete |
client.profiles | client.profiles | list, get, create, update, delete |
client.profiles.queue | client.profiles.queue | get, set, slots |
client.accounts | client.accounts | list, get, delete, boards, tiktokCreator (Python: tiktok_creator) |
client.platforms | client.platforms | list, get |
client.platformInvites | client.platform_invites | list, create, delete (Connect-Links) |
client.webhooks | client.webhooks | list, get, create, update, delete, test |
client.apiKeys | client.api_keys | list, create, delete |
Konfiguration
| Node.js | Python | Standard |
|---|---|---|
apiKey | api_key | FEEDHORIZON_API_KEY |
baseURL | base_url | FEEDHORIZON_BASE_URL, sonst https://feedhorizon.com/api/v1 |
timeout | timeout | 60 Sekunden (60000 ms in Node.js) |
maxRetries | max_retries | 2 |
defaultHeaders | default_headers | keine |
fetch | http_client | globales fetch / ein neuer httpx-Client |
Andere Sprachen
Jeder Endpunkt, jedes Feld und jeder Webhook-Payload steht in /openapi.json (OpenAPI 3.1). Nutz die Datei mit einem beliebigen OpenAPI-Client-Generator, oder ruf die API mit einfachem HTTP auf, wie die cURL-Beispiele zeigen.
Versionen
Die SDKs folgen Semantic Versioning. Neue API-Felder und Endpunkte kommen in Minor-Releases; was die API selbst zusagt, steht unter Versionierung. Release Notes findest du im CHANGELOG und in den GitHub-Releases jedes Repositorys.