// C1K REST API with fetch (Node.js 18 or newer, or a browser-less runtime). API-09. // C1K_API_KEY=c1k_rest_... C1K_ORG_ID=org_... node docs/examples/api.mjs const key = process.env.C1K_API_KEY; const org = process.env.C1K_ORG_ID; if (!key || !org) throw new Error('Set C1K_API_KEY and C1K_ORG_ID'); const base = `https://c1k.me/api/v1/orgs/${org}`; async function call(method, path, { body, headers = {} } = {}) { const res = await fetch(base + path, { method, headers: { Authorization: `Bearer ${key}`, ...(body ? { 'Content-Type': 'application/json' } : {}), ...headers, }, body: body ? JSON.stringify(body) : undefined, }); const payload = await res.json(); if (!res.ok) { const e = payload.error; const retry = res.headers.get('Retry-After'); throw new Error(`${res.status} ${e.code}: ${e.message}${retry ? ` (retry after ${retry}s)` : ''} [${e.request_id}]`); } return { payload, etag: res.headers.get('ETag') }; } // Create with an idempotency key, so a retry after a lost response cannot duplicate the link. const idem = `example-${crypto.randomUUID()}`; const { payload: created, etag } = await call('POST', '/links', { body: { destination_url: 'https://venture.example/launch', title: 'Launch page', utm: { source: 'newsletter', medium: 'email' } }, headers: { 'Idempotency-Key': idem }, }); console.log('created', created.data.short_url, 'version', created.data.version); // Edit using the version from the ETag (If-Match, API-07). const { payload: edited } = await call('PATCH', `/links/${created.data.id}`, { body: { title: 'Launch page (v2)' }, headers: { 'If-Match': etag }, }); console.log('edited to version', edited.data.version); // List every page. let cursor = null; do { const { payload } = await call('GET', `/links?limit=100${cursor ? `&cursor=${encodeURIComponent(cursor)}` : ''}`); for (const link of payload.data) console.log(link.short_url, '->', link.destination_url); cursor = payload.meta.next_cursor; } while (cursor); // Analytics of the organization and of one link. const { payload: stats } = await call('GET', '/analytics'); console.log('recorded requests', stats.data.totals.total, `(${stats.data.unique_estimate.label}: ${stats.data.unique_estimate.value})`); // Poll the change feed once; store meta.next_cursor for the next poll. const { payload: feed } = await call('GET', '/events'); for (const event of feed.data) console.log(event.id, event.time, event.action, event.target.id); console.log('next cursor', feed.meta.next_cursor);