This website uses cookies
We use cookies to personalise content, provide social features and analyse traffic with our analytics and marketing partners. Choose which optional cookies to allow. Cookie policy.
This website uses cookies
We use cookies to personalise content and improve your experience, to provide social media features and to analyse our traffic. We also share information about your use of our site with our analytics and marketing partners, who may combine it with other information that you have provided to them or that they have collected from your use of their services.
Cookie details
About these controls
You can choose which optional cookie categories to allow. Necessary cookies remain enabled because they support essential website functions. Read more in our cookie policy.
Browse documentation
Client examples
Client examples
Minimal TypeScript and Python clients with explicit error and retry handling.
Use an assigned HTTPS origin without /v1. These helpers return the complete API envelope. For writes, persist one idempotency key per logical operation; pass the same idempotency_key to every attempt, including retries after a process restart. Automatic HTTP retries are limited to errors the server marks as retryable; transport exceptions propagate to the caller. Install the Python requests package before using the example.
TypeScript:
const origin = process.env.CITERA_API_BASE_URL; if (!origin || !process.env.CITERA_API_KEY) throw new Error('Set CITERA_API_BASE_URL and CITERA_API_KEY'); const BASE = `${origin.replace(/\/$/, '')}/v1`; export async function citera<T>(path: string, init: RequestInit = {}): Promise<T> { const headers = new Headers(init.headers); headers.set("Authorization", `Bearer ${process.env.CITERA_API_KEY}`); if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json"); const res = await fetch(`${BASE}${path}`, { ...init, headers }); const body = await res.json(); if (!res.ok) { const err = new Error(`${body.error.code}: ${body.error.message} (request ${body.request_id})`); Object.assign(err, { status: res.status, retryable: body.error.retryable, retryAfter: res.headers.get('Retry-After') }); throw err; } return body as T; }
const origin = process.env.CITERA_API_BASE_URL; if (!origin || !process.env.CITERA_API_KEY) throw new Error('Set CITERA_API_BASE_URL and CITERA_API_KEY'); const BASE = `${origin.replace(/\/$/, '')}/v1`; export async function citera<T>(path: string, init: RequestInit = {}): Promise<T> { const headers = new Headers(init.headers); headers.set("Authorization", `Bearer ${process.env.CITERA_API_KEY}`); if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json"); const res = await fetch(`${BASE}${path}`, { ...init, headers }); const body = await res.json(); if (!res.ok) { const err = new Error(`${body.error.code}: ${body.error.message} (request ${body.request_id})`); Object.assign(err, { status: res.status, retryable: body.error.retryable, retryAfter: res.headers.get('Retry-After') }); throw err; } return body as T; }
const origin = process.env.CITERA_API_BASE_URL; if (!origin || !process.env.CITERA_API_KEY) throw new Error('Set CITERA_API_BASE_URL and CITERA_API_KEY'); const BASE = `${origin.replace(/\/$/, '')}/v1`; export async function citera<T>(path: string, init: RequestInit = {}): Promise<T> { const headers = new Headers(init.headers); headers.set("Authorization", `Bearer ${process.env.CITERA_API_KEY}`); if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json"); const res = await fetch(`${BASE}${path}`, { ...init, headers }); const body = await res.json(); if (!res.ok) { const err = new Error(`${body.error.code}: ${body.error.message} (request ${body.request_id})`); Object.assign(err, { status: res.status, retryable: body.error.retryable, retryAfter: res.headers.get('Retry-After') }); throw err; } return body as T; }
const origin = process.env.CITERA_API_BASE_URL; if (!origin || !process.env.CITERA_API_KEY) throw new Error('Set CITERA_API_BASE_URL and CITERA_API_KEY'); const BASE = `${origin.replace(/\/$/, '')}/v1`; export async function citera<T>(path: string, init: RequestInit = {}): Promise<T> { const headers = new Headers(init.headers); headers.set("Authorization", `Bearer ${process.env.CITERA_API_KEY}`); if (init.body && !headers.has("Content-Type")) headers.set("Content-Type", "application/json"); const res = await fetch(`${BASE}${path}`, { ...init, headers }); const body = await res.json(); if (!res.ok) { const err = new Error(`${body.error.code}: ${body.error.message} (request ${body.request_id})`); Object.assign(err, { status: res.status, retryable: body.error.retryable, retryAfter: res.headers.get('Retry-After') }); throw err; } return body as T; }
Python:
import os, time, random, requests BASE = os.environ["CITERA_API_BASE_URL"].rstrip("/") + "/v1" S = requests.Session() S.headers["Authorization"] = f"Bearer {os.environ['CITERA_API_KEY']}" def call(method, path, *, json=None, idempotency_key=None, attempts=5): # The caller persists one key per logical write, including across restarts. headers = {"Idempotency-Key": idempotency_key} if idempotency_key else {} for i in range(attempts): r = S.request(method, BASE + path, json=json, headers=headers, timeout=30) body = r.json() if r.ok: return body if body["error"]["retryable"] and i < attempts - 1: time.sleep(float(r.headers.get("Retry-After", 2 ** i)) + random.uniform(0, 0.5)) continue raise RuntimeError(f"{body['error']['code']}: {body['error']['message']} ({body['request_id']})")
import os, time, random, requests BASE = os.environ["CITERA_API_BASE_URL"].rstrip("/") + "/v1" S = requests.Session() S.headers["Authorization"] = f"Bearer {os.environ['CITERA_API_KEY']}" def call(method, path, *, json=None, idempotency_key=None, attempts=5): # The caller persists one key per logical write, including across restarts. headers = {"Idempotency-Key": idempotency_key} if idempotency_key else {} for i in range(attempts): r = S.request(method, BASE + path, json=json, headers=headers, timeout=30) body = r.json() if r.ok: return body if body["error"]["retryable"] and i < attempts - 1: time.sleep(float(r.headers.get("Retry-After", 2 ** i)) + random.uniform(0, 0.5)) continue raise RuntimeError(f"{body['error']['code']}: {body['error']['message']} ({body['request_id']})")
import os, time, random, requests BASE = os.environ["CITERA_API_BASE_URL"].rstrip("/") + "/v1" S = requests.Session() S.headers["Authorization"] = f"Bearer {os.environ['CITERA_API_KEY']}" def call(method, path, *, json=None, idempotency_key=None, attempts=5): # The caller persists one key per logical write, including across restarts. headers = {"Idempotency-Key": idempotency_key} if idempotency_key else {} for i in range(attempts): r = S.request(method, BASE + path, json=json, headers=headers, timeout=30) body = r.json() if r.ok: return body if body["error"]["retryable"] and i < attempts - 1: time.sleep(float(r.headers.get("Retry-After", 2 ** i)) + random.uniform(0, 0.5)) continue raise RuntimeError(f"{body['error']['code']}: {body['error']['message']} ({body['request_id']})")
import os, time, random, requests BASE = os.environ["CITERA_API_BASE_URL"].rstrip("/") + "/v1" S = requests.Session() S.headers["Authorization"] = f"Bearer {os.environ['CITERA_API_KEY']}" def call(method, path, *, json=None, idempotency_key=None, attempts=5): # The caller persists one key per logical write, including across restarts. headers = {"Idempotency-Key": idempotency_key} if idempotency_key else {} for i in range(attempts): r = S.request(method, BASE + path, json=json, headers=headers, timeout=30) body = r.json() if r.ok: return body if body["error"]["retryable"] and i < attempts - 1: time.sleep(float(r.headers.get("Retry-After", 2 ** i)) + random.uniform(0, 0.5)) continue raise RuntimeError(f"{body['error']['code']}: {body['error']['message']} ({body['request_id']})")