CiteFlow API

SDKs

Official Node.js and Python clients with retry, idempotency, and webhook helpers built in.

CiteFlow ships two official SDKs that wrap the REST API with retry policies, automatic Idempotency-Key generation, polling helpers, and HMAC webhook verification. Use them and you can stop reading half the rest of these docs.

RuntimePackageVersions
Node.js 18+@citeflow/sdkv0.1.0
Python 3.9+citeflow-pythonv0.1.0

Both SDKs follow Semantic Versioning and track the v1 API. Breaking changes mint v1v2 and the SDK majors move with it.

Install

Node

npm install @citeflow/sdk
# pnpm add @citeflow/sdk
# yarn add @citeflow/sdk

Python

pip install citeflow-python

Hello world

Node

import { Citeflow } from '@citeflow/sdk';

const client = new Citeflow({ apiKey: process.env.CITEFLOW_API_KEY! });

const audit = await client.audits.create({
  url: 'https://example.com',
  type: 'seo',
});

const result = await client.audits.waitForCompletion(audit.audit_id, {
  timeoutMs: 90_000,
});

console.log(result.status, result.status === 'complete' && result.scores);

Python

import os
from citeflow import Citeflow

client = Citeflow(api_key=os.environ["CITEFLOW_API_KEY"])

audit = client.audits.create(url="https://example.com", type="seo")
result = client.audits.wait_for_completion(audit["audit_id"], timeout=90)

print(result["status"], result.get("scores"))

What the SDK does for you

ConcernWithout SDKWith SDK
Auth headermanual Authorization: Bearer …new Citeflow({ apiKey })
Idempotencymint UUID per POST, store, retry-safeautomatic per call
Retry on 429/5xxexp backoff + jitter + Retry-After honoringbuilt in (maxRetries: 3)
Polling for auditscustom loop with timeout + intervalwaitForCompletion / wait_for_completion
Webhook signingparse 3 headers, HMAC-SHA256, base64, timing-safe compare, rotation grace, ±5min skewverifyWebhookSignature / verify_webhook_signature
Error envelopeparse JSON, branch on error.codecatch CiteflowError with typed .code, .requestId

Webhook verification

The SDKs handle the part most partners get wrong on a first integration.

Node

import { parseWebhookEvent, CiteflowSignatureError } from '@citeflow/sdk';

// In an Express / Next.js Route Handler:
const rawBody = await readRawBody(req); // do NOT JSON-parse first
try {
  const event = parseWebhookEvent({
    rawBody,
    headers: req.headers,
    secret: process.env.CITEFLOW_WEBHOOK_SECRET!,
  });
  // event.type ∈ { 'audit.completed', 'audit.failed', 'audit.cancelled', 'balance.low' }
  await processEvent(event);
  res.status(200).end();
} catch (err) {
  if (err instanceof CiteflowSignatureError) return res.status(400).end();
  throw err;
}

Python

from flask import request
from citeflow import parse_webhook_event, CiteflowSignatureError

@app.post("/citeflow-webhook")
def webhook():
    try:
        event = parse_webhook_event(
            raw_body=request.get_data(),  # bytes
            headers=request.headers,
            secret=os.environ["CITEFLOW_WEBHOOK_SECRET"],
        )
        process_event(event)
        return "", 200
    except CiteflowSignatureError:
        return "", 400

The verifier handles the rotation grace window automatically — when you rotate the signing secret in the dashboard, both old and new signatures verify against your old secret for 7 days, then both verify against the new one.

Error handling

Both SDKs raise typed errors on non-2xx responses. Branch on code (stable) rather than HTTP status (relaxable).

import { CiteflowError } from '@citeflow/sdk';

try {
  await client.audits.create({ url: '…', type: 'all' });
} catch (err) {
  if (err instanceof CiteflowError) {
    if (err.code === 'INSUFFICIENT_CREDITS') {
      // err.required tells you how many credits short you are.
      console.warn(`Need ${err.required} more credits`);
    }
    console.error(err.requestId); // include in support tickets
  }
}
from citeflow import CiteflowError

try:
    client.audits.create(url="…", type="all")
except CiteflowError as err:
    if err.code == "INSUFFICIENT_CREDITS":
        print(f"Need {err.required} more credits")
    print(err.request_id)

Configuration knobs

new Citeflow({
  apiKey: 'ckf_…',
  baseUrl: 'https://www.citeflow.io/api/v1', // staging override
  timeoutMs: 30_000,
  maxRetries: 3,
});
Citeflow(
  api_key="ckf_…",
  base_url="https://www.citeflow.io/api/v1",
  timeout=30.0,
  max_retries=3,
  session=my_requests_session,   # custom adapter / proxy
)

Source

  • Node SDK: packages/sdk-node/ in the CiteFlow monorepo.
  • Python SDK: packages/sdk-python/.
  • Issues / requests: partners@citeflow.io.

On this page