Onyx API documentation.
Read-only access to every public rating, case, and framework version. Base URL https://api.onyx.dev. JSON in, JSON out, signed on the way. For plans and fair-use rules, see API & data access.
#Introduction
The API serves exactly what the site shows — the public dataset, nothing privileged. There is no write API: scores are produced by the review desk and published; callers read. Responses are UTF-8 JSON. All timestamps are ISO-8601 UTC. All identifiers are stable slugs, never numeric IDs.
curl https://api.onyx.dev/v1/projects?limit=3 \
-H "Authorization: Bearer onyx_pk_····"
#Authentication
Pass your key in the Authorization header on every request. Keys are prefixed by tier: onyx_pk_ (free), onyx_pro_, onyx_ent_. Keys in query strings are rejected with 401 key_in_url — they end up in logs, and we'd rather fail loudly than leak quietly.
Authorization: Bearer onyx_pro_4f8a····e21c
#Pagination
List endpoints are cursor-paginated. Pass limit (max 100) and follow next_cursor until it is null. Cursors are stable across score updates — a paginating client never sees a project twice or misses one because ranks shifted mid-walk.
{
"data": [ … ],
"next_cursor": "eyJyYW5rIjoyNH0",
"total": 2418
}
#Rate limits
Limits are per key, per rolling 24 hours: 1,000 (Free), 100,000 (Pro), unmetered (Enterprise). Every response carries the remaining budget; exceeding it returns 429 with a Retry-After header. Limits gate volume only — no tier ever sees different data.
X-RateLimit-Limit: 100000 X-RateLimit-Remaining: 99871 X-RateLimit-Reset: 2026-07-12T00:00:00Z
#Errors
Errors are JSON with a stable machine code and a human sentence. The code is the contract; the message may improve without notice.
| 400 invalid_cursor | The cursor is malformed or from an old dataset generation. Restart the walk. |
| 401 key_missing / key_revoked | No key, or a key that has been rotated or revoked. |
| 403 plan_required | The endpoint exists but needs a higher tier — the response names it. |
| 404 unknown_slug | No project by that slug. Removed projects still resolve — with their removal case attached. |
| 410 project_removed | Only on history ranges after a removal date; the rating no longer exists past that point. |
| 429 rate_limited | Daily budget exhausted. Honor Retry-After. |
#Verifying signatures
Every response body is signed with the Onyx publishing key (Ed25519). The signature covers the raw body bytes plus the Date header — so a stored payload can prove, later, what we published and when. The public key is pinned at /disclosures and served from DNS.
const ok = crypto.verify("ed25519", body, ONYX_PUBLIC_KEY, Buffer.from(res.headers["x-onyx-signature"], "base64")); // ok === true → this is a real Onyx score
The ratings directory. Sorted by rank by default. Every filter the site's directory has is a query parameter here.
| Name | Type | Description |
|---|---|---|
| categoryoptional | string | One of the nine category slugs, e.g. dex, lending, gamefi. |
| tieroptional | string | certified · verified · listed · provisional. Repeatable. |
| min_scoreoptional | integer | 0–100. Combined with max_score for a range. |
| watchlistoptional | boolean | true returns only projects with an open case. |
| limitoptional | integer | 1–100, default 25. |
| cursoroptional | string | From the previous page's next_cursor. |
The full rating object — everything on the project page. Fields a project chose not to provide are null, never omitted: absence is data here, and the shape is stable regardless.
| Field | Type | Description |
|---|---|---|
| score | integer | 0–100, under the methodology version in methodology. |
| tier | string | Derived from score plus tier minimums — not score alone. |
| criteria | object | All 14 sub-criteria with awarded points and category maximums. |
| watchlist | object · null | The open case (action type, case ID, dates) or null. |
| audits | array | Firm, date, scope, report link. Empty array if none published. |
| bug_bounty | object · null | null when not provided — never lowers the score. |
| entity | object | Legal status: verified jurisdiction, pseudonymous, or unresolved. |
Score time series. Each point carries the methodology version it was scored under — framework changes re-label history, they never silently re-score it.
{ "date": "2026-06-14", "score": 94, "tier": "certified",
"methodology": "v3.2", "event": "scheduled_review" }
All public cases, newest first. The case shape matches the public ledger: project, before/after score, action type, dated key facts, the project's response (or documented non-response), evidence links, reviewer of record, case ID.
| Name | Type | Description |
|---|---|---|
| actionoptional | string | downgrade · suspension · removal · reinstatement. Repeatable. |
| statusoptional | string | open or resolved. |
| sinceoptional | date | Cases opened on or after this date. |
The App Store directory — every reviewed-and-listed app. Detail at /v1/apps/{slug} returns the full listing: review record, permissions, on-chain usage (Onyx-indexed), version history, and wallet-verified review summary. Developer-supplied fields follow the platform rule — null when not provided, never omitted, never penalized.
| Name | Type | Description |
|---|---|---|
| categoryoptional | string | App category slug, e.g. wallet, dapp, analytics. |
| statusoptional | string | listed (default) · delisted. Delisted apps stay queryable with their delisting reason attached. |
| chainoptional | string | Filter by supported chain. Repeatable. |
| rated_projectoptional | boolean | true returns only apps whose developer has a rated project. |
| Field | Type | Description |
|---|---|---|
| review | object | Review record — standard version, decision date, reviewer of record, automated-check results. |
| permissions | array | Requested contract approvals and scopes, as verified at review. |
| usage | object · null | Onyx-indexed on-chain usage — active wallets, transactions. null for off-chain apps. |
| versions | array | Version history with re-review status per release. |
| sponsored | boolean | true only while a paid placement is active — the flag mirrors the on-site label, so consumers can (and must) disclose it too. |
Marketplace providers — audit firms, security teams, legal counsel. Detail at /v1/providers/{slug} adds service lines with published pricing, the Onyx-measured response/delivery record, sample work, and client reviews. Pricing and team data are provider-supplied and therefore nullable.
| Name | Type | Description |
|---|---|---|
| specializationoptional | string | e.g. smart-contract-audit, incident-response, legal. Repeatable. |
| min_engagementsoptional | integer | Only providers with at least this many verified engagements. |
| publishes_pricingoptional | boolean | true returns only providers with published pricing. |
| Field | Type | Description |
|---|---|---|
| services | array | Service lines; each with pricing (object · null when unpublished). |
| record | object | Onyx-measured — median response time, on-time delivery rate, engagements verified. Measured by us, not self-reported. |
| reports | array | Published engagement reports with links; empty when none are public. |
| reviews | object · null | Wallet-verified client review summary — count, distribution. |
| team | array · null | Named team members and credentials, where the provider discloses them. |
The framework as config: criteria, weights, tier thresholds and minimums, plus the diff against the prior version. latest resolves to the current version — v3.2 today.
The entire public dataset as gzipped JSONL, regenerated daily at 00:30 UTC — and immediately after any correction. The manifest carries a signed checksum per file; verify before you ingest.
{ "generated": "2026-07-11T00:30:04Z",
"files": [ { "name": "projects.jsonl.gz", "sha256": "9f2c····", "rows": 2418 } ],
"signature": "base64····" }
Websocket feed of publication events — score changes, new and resolved cases, tier moves, corrections — emitted at the moment they go public. The same events are available as signed webhooks. Nothing streams before it publishes: subscribers get speed, not foresight.
{ "type": "score_change", "slug": "emberswap",
"from": 71, "to": 42, "case": "ONX-2026-0114",
"published": "2026-07-08T14:02:11Z" }