Publish a living time series
Humans and agents share the same normalized API. Discover a measurement, inspect its data card, read its history, then follow it. Creators define a private draft, push observations with evidence, and publish when the measurement is ready.
API base: https://zernel.de. Every identifier accepts a stable slug or UUID. Authenticate writes and private reads with Authorization: Bearer $ZERNEL_TOKEN. Sign in with GitHub, then generate a 30-day API token from your account page. Tokens are shown once and can be revoked on your account page. To rotate, create a replacement, update your agent’s ZERNEL_TOKEN, then revoke the old token. Each token has your account’s access to its series. Email/password authentication is disabled in production. Browser access uses a secure HttpOnly Better Auth session cookie.
1 / Create a private measurement
curl "$ZERNEL_API/v1/series" -X POST \
-H "Authorization: Bearer $ZERNEL_TOKEN" -H "Content-Type: application/json" \
-d '{"slug":"my-widget-count","title":"Widget count","description":"A daily measurement from my own counter.","unit":"widgets","cadence":"daily","visibility":"private","accessModel":"free"}'2 / Describe the measurement and its rights
curl "$ZERNEL_API/v1/series/my-widget-count/card" -X PUT \
-H "Authorization: Bearer $ZERNEL_TOKEN" -H "Content-Type: application/json" \
-d '{"entity":"My counter","metric":"Widget count","license":"CC0-1.0","attribution":"My organization","sourceUrl":"https://example.org/counter","methodology":"Read the integer counter once per UTC day. Preserve missing observations.","coverage":"complete","observationFrequency":"daily","redistribution":"allowed"}'3 / Push actual observations
Hash the retrieved source evidence with SHA-256. Each observation preserves its source URL, provider, retrieval time and hash. A creator-provided hash is an attestation, not independent verification. Store the underlying evidence in your collector or Zernel's ingestion evidence bucket.
curl "$ZERNEL_API/v1/series/my-widget-count/observations" -X POST \
-H "Authorization: Bearer $ZERNEL_TOKEN" -H "Content-Type: application/json" \
-d '{"observations":[{"timestamp":"2026-10-01T00:00:00.000Z","value":42,"provenance":{"provider":"My counter","sourceUrl":"https://example.org/counter","sourceHash":"73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049","retrievedAt":"2026-10-01T01:00:00.000Z"}}]}'Push up to 10,000 observations per batch. Retrying an unchanged canonical value with the same evidence hash is a no-op. Corrected values append a revision. JSON/CSV/NDJSON bulk upload through /data accepts up to 100,000 rows and supports dry-run validation.
4 / Publish, discover, consume
curl "$ZERNEL_API/v1/series/my-widget-count/publish" -X POST \
-H "Authorization: Bearer $ZERNEL_TOKEN"
curl "$ZERNEL_API/v1/catalog/series?q=widgets"
curl "$ZERNEL_API/v1/series/my-widget-count/data?format=json&limit=1000"
curl "$ZERNEL_API/v1/series/my-widget-count/export?format=csv"Publication requires a complete data card, redistribution allowed, and a canonical observation. Free published series are anonymously readable. Private drafts remain owner-only. Use nextCursor as the next request's after; CSV/NDJSON expose the cursor in x-next-cursor. Pagination is not a snapshot of concurrent writes.
Clone & stream
Open a series and choose Connect. Clone downloads history into a local JSON file; Stream keeps that copy current while the command runs on your computer. Python 3.9 or newer is the only requirement. Set ZERNEL_WEB to https://zernel.de and replace the example slug:
curl -fsS "$ZERNEL_WEB/zernel.py" -o zernel.py
python3 zernel.py clone "$ZERNEL_WEB/series/my-widget-count"
python3 zernel.py stream "$ZERNEL_WEB/series/my-widget-count"Private or restricted reads require your own ZERNEL_TOKEN environment variable. The token is never stored in the output. Stream polls the change feed every 30 seconds, retries temporary failures and resumes from its saved cursor. Stop with Ctrl+C. Your computer and the command must stay running to receive updates. Retraction markers remain in the file: filter status != "retracted" for current values. Clone merges available projected history with accepted ledger changes; concurrent writes mean it is not an atomic database snapshot.
For your own client, paginate /data with its timestamp cursor, then replay /changes from commit cursor 0 and retain the highest revision for each timestamp. A response provides items, nextCursor and hasMore. Drain all pages before polling again. Persist data and cursor together; using only the newest observation timestamp misses corrections to old periods. Change events can precede chart projection.
Discovery ranking
The frontpage offers maximum abnormality over the last 7 days or alphabetical order. Abnormality is the maximum absolute sample z-score against the preceding 30 observations within 120 days, with at least 10 baseline observations and nonzero variance. Missing, restricted, insufficient and flat histories are unranked. The score uses all recent canonical observations before sparkline truncation; a high score is a departure from the baseline, not a forecast. The 7-day percentage change is undefined when its first value is zero.
Revisions & collection
/revisions/{timestamp}?asOf={knowledge-time} returns the revisions known by that insertion time. Corrections and retractions preserve earlier versions. Collection cadence and observation frequency are separate: annual population can be checked monthly. Creation and publication never enable collection. Deterministic collectors require an explicit configuration and a running scheduler; otherwise your own agent must push updates.
Managing your series
Use GET /v1/me/series to list your drafts and published measurements. Sign in with GitHub and open your account page to see your series and generate an API token. Publishing is API-only; the Publish page prepares instructions for your agent. The Operations web screen is currently disabled. Owner-only collector health, proposal review and change APIs remain available.
Provider coverage & economics
Public pages often contain only a release preview. Trading Economics guest API access is discontinued; complete history requires a licensed key. Open World Bank examples are publicly published on Zernel. Source inventory is available at /v1/sources. Paid checkout and payouts are not yet enabled; supplying a paid tier cannot grant production access.
REST reference
| Method | Route | Purpose |
|---|---|---|
GET | /.well-known/zernel.json | Agent discovery manifest and API limits |
GET | /v1/agent/schema | Measurement schema, push/pull and retry semantics |
GET | /v1/catalog/series?q=population | Discover public measurements |
GET | /v1/catalog/board?limit=500&window=120 | Batch quotes and 7-day abnormality |
GET | /v1/series/{id}/changes?after=0&limit=1000 | Commit-ordered observations, corrections and retractions |
GET | /v1/sources | Source access, coverage and distribution requirements |
POST | /v1/series | Create a creator-owned private draft |
GET | /v1/me/series | Your private drafts and published series |
PUT | /v1/series/{id}/card | Set source, license, entity, metric and methodology |
POST | /v1/series/{id}/observations | Push evidenced observations; retry-safe |
POST | /v1/series/{id}/publish | Publish a free attributed series |
POST | /v1/series/{id}/unpublish | Restore a private draft |
GET | /v1/series/{id}/data?limit=1000 | Paginated raw JSON, CSV or NDJSON |
POST | /v1/series/{id}/data?dryRun=true | Validate JSON, CSV or NDJSON before writing |
GET | /v1/series/{id}/revisions/{timestamp}?asOf= | Audit changes and look up a vintage |
GET | /v1/series/{id}/datapoints?from=&to=&resolution=raw | Bounded chart-ready observations |
GET | /v1/series/{id}/export?format=csv | Full-history CSV/JSON export |
POST | /v1/series/{id}/subscriptions | Follow a free series as the signed-in user |
POST | /v1/series/{id}/runs | Collect using configured Temporal providers |
GET | /health | Dependency, run and quality health |
Machine-readable OpenAPI is served at /openapi.json; agent discovery starts at /.well-known/zernel.json.