takt

SDK de lecture

@vskstudio/takt-read est un SDK TypeScript côté serveur au-dessus de l'API HTTP de lecture. Il encapsule les endpoints /api/v1/sites/{domain}/stats/* (authentifiés par clé Bearer) dans des méthodes typées, pour rapatrier tes statistiques dans un backend, un cron ou une route d'API sans écrire le HTTP à la main.

pnpm add @vskstudio/takt-read

Il a zéro dépendance runtime, fournit ESM + CJS et nécessite Node.js ≥ 20 (fetch natif).

Ta clé d'API de lecture est secrète. N'utilise ce SDK que côté serveur. Ne l'embarque jamais dans un bundle navigateur.
Ce SDK reste en lecture seule, et c'est un choix : il ne créera ni objectif, ni site, ni invitation. Pour écrire (sites, objectifs, entonnoirs, segments, alertes, membres), passe par le serveur MCP distant, autorisé en OAuth et borné à ton rôle.

Démarrage rapide

import TaktClient from '@vskstudio/takt-read'

const takt = new TaktClient({
  apiKey: process.env.TAKT_API_KEY!, // secret: à garder côté serveur
  domain: 'exemple.com',
  org: 'mon-org', // optionnel, requis seulement par stats.export() et stats.exportResult()
  timeoutMs: 30_000, // optionnel, 30s par défaut
  retries: 2 // optionnel, 2 par défaut (réessaie 429 + 5xx avec backoff)
})

const resume = await takt.stats.summary({ period: '30d' })
console.log(resume.visitors, resume.pageviews)

baseUrl est optionnel : il vaut par défaut la racine de l’API de lecture hébergée (https://taktlytics.com/api/v1). Renseigne-le pour viser un autre hôte (un proxy first-party, un environnement de test) en gardant le préfixe /api/v1, par exemple https://proxy.exemple.com/api/v1. Le http:// simple n’est accepté que sur la boucle locale (localhost, 127.0.0.1, [::1]) ; tout autre hôte doit être en https://. Le constructeur valide ses options et lève une TaktError (code: 'config_invalide') en cas d’entrée invalide.

Options de TaktClient :

OptionTypeDéfautRôle
apiKeystringaucunClé d’API avec la permission stats:read
domainstringaucunSite interrogé (lié à la clé)
orgstringaucunSlug de l’org propriétaire de domain ; requis seulement par stats.export() et stats.exportResult()
baseUrlstringhttps://taktlytics.com/api/v1Racine de l’API de lecture (…/api/v1)
fetchtypeof fetchfetch globalImplémentation de fetch personnalisée
timeoutMsnumber30000Délai par requête
retriesnumber2Réessais sur 429/5xx/réseau avec backoff

Méthodes de stats

Tous les endpoints de lecture vivent sous takt.stats. Chaque méthode accepte un dernier argument optionnel { signal } pour l’annulation via un AbortController.

MéthodeRenvoie
stats.summary(query?)StatsSummary
stats.timeseries(query?)StatsTimeseries
stats.breakdown(query & { dimension })StatsBreakdown
stats.breakdowns(dimensions, query?)StatsBreakdowns
stats.realtime()StatsRealtime
stats.goals(query?)StatsGoals
stats.funnels(query?)FunnelReports
stats.properties(event, query?)string[]
stats.propertyBreakdown(event, key, query?)PropertyBreakdown
stats.propertyBreakdownBatch(request, query?)PropertyBatchResponse
stats.revenue(event, query?)RevenueByCurrency
stats.export(query?)string (CSV) ou StatsExportRow[] (JSON)
stats.exportResult(query?)StatsExportResult<string> (CSV) ou StatsExportResult<StatsExportRow[]> (JSON)
const ac = new AbortController()
const realtime = await takt.stats.realtime({ signal: ac.signal })

// Plusieurs classements en une requête
const { breakdowns } = await takt.stats.breakdowns(['pages', 'sources'], { period: '30d' })

// Export du classement des pages (nécessite `org` sur le client)
const csv = await takt.stats.export({ period: '30d' }) // chaîne CSV brute
const rows = await takt.stats.export({ period: '30d', format: 'json' }) // StatsExportRow[]

L’export est borné côté serveur à 999 lignes et 256 Ko, en CSV comme en JSON. Quand la borne coupe des lignes, l’API pose l’en-tête X-Takt-Truncated: true et le corps reste une chaîne CSV ou un tableau JSON. stats.export() renvoie ce corps tel quel. stats.exportResult(), disponible depuis takt-read 0.6.0, renvoie { data, truncated } pour savoir s’il manque des lignes :

const { data, truncated } = await takt.stats.exportResult({ period: '30d', format: 'json' })
if (truncated) {
  // data ne contient que les premières lignes : resserrez la période ou le segment
}

truncated vaut false si l’en-tête vaut false ou est absent. data a le même type que le résultat de stats.export() pour le même format.

Options de requête

Les méthodes de lecture acceptent un StatsQuery optionnel : une period (day, 7d, 30d, month, 6mo, 12mo) ou un intervalle explicite from/to, plus tz, country, interval, compare, limit et des filtres segment.

await takt.stats.timeseries({
  period: '30d',
  interval: 'day',
  tz: 'Europe/Paris',
  compare: true, // inclut la période précédente
  segment: [
    { dim: 'browsers', op: 'is', val: 'Firefox' },
    { dim: 'pages', op: 'not', val: '/blog', join: 'or' }
  ]
})

dimension et segment[].dim prennent une StatsDimension : pages, sources, countries, regions, cities, os, browsers, devices, utm_source, utm_medium, utm_campaign (au pluriel : page ou source ne sont pas valides). segment[].op vaut is ou not.

Gestion des erreurs

Les réponses non-2xx lèvent une TaktError portant le statut HTTP et le code d’erreur de l’API. Le SDK lève aussi config_invalide sur des options invalides, timeout quand une requête dépasse timeoutMs, et erreur_reseau en cas de panne réseau.

import { TaktError } from '@vskstudio/takt-read'

try {
  await takt.stats.summary()
} catch (err) {
  if (err instanceof TaktError && err.code === 'trop_de_requetes') {
    await new Promise((resolve) => setTimeout(resolve, 10_000))
  }
}
Le SDK consomme la même API de lecture que les appels HTTP directs, incluse sans limite de volume. Au-delà de 200 requêtes par 10 secondes ou de 600 par minute pour une même clé, les endpoints répondent 429 (trop_de_requetes) : attends le délai de l'en-tête Retry-After avant de réessayer. En amont, la passerelle plafonne aussi chaque adresse IP à 20 requêtes par seconde (pointes jusqu'à 100) : ce 429-là a un corps HTML et pas de Retry-After, attends une seconde. L'ingestion n'est jamais affectée. Tu préfères le langage naturel ? Vois le serveur MCP.