REST API
Public read-only API under /api/v1 — endpoints, parameters, example requests and responses, errors and rate limits.
The registry has a public, read-only JSON API. It is what the CLI uses, and you can use it for your own tools. No authentication is needed.
- Base URL:
https://denofy.com/api/v1 - Responses are JSON (
application/json) unless stated otherwise; timestamps are ISO 8601 in UTC. - Machine-readable description:
/api/v1/openapi.json(OpenAPI 3.1, generated from the same schemas as this page).
Endpoints#
Search packages#
GET/api/v1/search
Lists public packages that have at least one checked version. Without q the result is the full catalogue in the chosen order.
| Parameter | In | Type | Description |
|---|---|---|---|
q | query | string | Free text. Prefix match on name, title, summary, tags, category and description (up to 8 words). |
type | query | enum | Package type. One of: skill, rule, agents-md, subagent, command. Unknown values are ignored. |
tool | query | "claude-code" | "codex" | "cursor" | Only packages that support this tool. One of: claude-code, codex, cursor. Unknown values are ignored. |
category | query | enum | Category slug. One of: coding, testing, git, frontend, backend, devops, data, documentation, security, productivity, other. Unknown values are ignored. |
verified | query | "0" | "1" | "true" | "false" | 1 or true: only packages whose latest version is verified. |
sort | query | enum | Sort order. Default: relevance with a query, otherwise downloads. trending ranks by installs in the last 7 days. One of: relevance, downloads, newest, updated, rating, trending. Unknown values are ignored. |
page | query | integer (≥ 1)default 1 | Page number, starting at 1. |
limit | query | integer (1–100)default 20 | Results per page (1–100). |
curl "https://denofy.com/api/v1/search?q=commit&tool=cursor&verified=1"200 One page of results.
{
"total": 1,
"page": 1,
"limit": 20,
"items": [
{
"name": "commit-helper",
"title": "Commit Helper",
"summary": "Writes Conventional Commit messages from the staged diff.",
"type": "skill",
"category": "git",
"tags": [
"git",
"commits"
],
"tools": [
"claude-code",
"codex",
"cursor"
],
"author": "octocat",
"downloads": 1284,
"rating": {
"average": 4.6,
"count": 12
},
"latestVersion": "1.3.0",
"status": "verified",
"riskLevel": "none",
"updatedAt": "2026-10-01T09:30:00.000Z",
"url": "https://denofy.com/p/commit-helper"
}
]
}Package details#
GET/api/v1/packages/{name}
Metadata of a package: README (description), repository, license, rating and the latest version with its verification status.
| Parameter | In | Type | Description |
|---|---|---|---|
namerequired | path | string | Package name (slug), e.g. commit-helper. |
curl https://denofy.com/api/v1/packages/commit-helper200 The package.
{
"name": "commit-helper",
"title": "Commit Helper",
"summary": "Writes Conventional Commit messages from the staged diff.",
"description": "# Commit Helper\n\nReads the staged diff and proposes a commit message…",
"type": "skill",
"category": "git",
"tags": [
"git",
"commits"
],
"tools": [
"claude-code",
"codex",
"cursor"
],
"author": {
"username": "octocat",
"name": "The Octocat"
},
"repository": {
"url": "https://github.com/octocat/skills",
"path": "commit-helper",
"branch": "main"
},
"license": "MIT",
"icon": "https://denofy.com/api/media/icon/1f0c2b9e-mabc123.webp",
"links": {
"docs": "https://octocat.github.io/skills",
"issues": "https://github.com/octocat/skills/issues"
},
"downloads": 1284,
"favorites": 37,
"followers": 21,
"rating": {
"average": 4.6,
"count": 12
},
"latestVersion": "1.3.0",
"status": "verified",
"riskLevel": "none",
"archived": false,
"deprecated": null,
"premium": null,
"createdAt": "2026-06-12T08:00:00.000Z",
"updatedAt": "2026-10-01T09:30:00.000Z",
"url": "https://denofy.com/p/commit-helper"
}404 The package does not exist, is private or was hidden by the moderators.
{
"error": "Package \"nope\" not found"
}List versions#
GET/api/v1/packages/{name}/versions
All versions, newest first, without the full verification report. Blocked versions are left out; yanked versions are included with yanked: true.
| Parameter | In | Type | Description |
|---|---|---|---|
namerequired | path | string | Package name (slug), e.g. commit-helper. |
curl https://denofy.com/api/v1/packages/commit-helper/versions200 All versions.
{
"name": "commit-helper",
"latestVersion": "1.3.0",
"versions": [
{
"name": "commit-helper",
"type": "skill",
"tools": [
"claude-code",
"codex",
"cursor"
],
"version": "1.3.0",
"commitSha": "9f1c2e7d3b4a5c6d7e8f90a1b2c3d4e5f6a7b8c9",
"gitRef": "v1.3.0",
"changelog": "Support for scopes",
"status": "verified",
"riskLevel": "none",
"yanked": false,
"downloads": 311,
"createdAt": "2026-10-01T09:30:00.000Z",
"verifiedAt": "2026-10-01T09:30:04.000Z",
"files": [
{
"path": "SKILL.md",
"size": 167,
"sha256": "84108285e5634549ce6cfeabf9ccc548b88b0ef4f9d3d721e847aa3132197e64"
}
],
"tarball": {
"url": "https://denofy.com/api/v1/packages/commit-helper/versions/1.3.0/download",
"sha256": "3b5d5c3712955042212316173ccf37be800ea6b9b4d2f3e6b3e1c3d2a1b0c9d8",
"size": 742
},
"installTargets": {
"claude-code": {
"mode": "directory",
"project": ".claude/skills/commit-helper",
"global": ".claude/skills/commit-helper"
},
"codex": {
"mode": "directory",
"project": ".agents/skills/commit-helper",
"global": ".codex/skills/commit-helper"
},
"cursor": {
"mode": "skill-to-mdc",
"project": ".cursor/rules",
"global": null
}
},
"summary": "All 8 checks passed.",
"archived": false,
"deprecated": null,
"premium": null,
"moderation": null
}
]
}404 The package does not exist, is private or was hidden by the moderators.
{
"error": "Package \"nope\" not found"
}Resolve a version#
GET/api/v1/packages/{name}/versions/{version}
Resolves latest, an exact version or a semver range and returns the version including the full verification report. This is what the CLI calls before downloading.
| Parameter | In | Type | Description |
|---|---|---|---|
namerequired | path | string | Package name (slug), e.g. commit-helper. |
versionrequired | path | string | Exact version (1.2.0), latest, or a URL-encoded semver range (%5E1.2.0 for ^1.2.0). Ranges resolve to the highest matching version that is not yanked or blocked. |
curl https://denofy.com/api/v1/packages/commit-helper/versions/%5E1.2.0200 The resolved version with report.
{
"name": "commit-helper",
"type": "skill",
"tools": [
"claude-code",
"codex",
"cursor"
],
"version": "1.3.0",
"commitSha": "9f1c2e7d3b4a5c6d7e8f90a1b2c3d4e5f6a7b8c9",
"gitRef": "v1.3.0",
"changelog": "Support for scopes",
"status": "verified",
"riskLevel": "none",
"yanked": false,
"downloads": 311,
"createdAt": "2026-10-01T09:30:00.000Z",
"verifiedAt": "2026-10-01T09:30:04.000Z",
"files": [
{
"path": "SKILL.md",
"size": 167,
"sha256": "84108285e5634549ce6cfeabf9ccc548b88b0ef4f9d3d721e847aa3132197e64"
}
],
"tarball": {
"url": "https://denofy.com/api/v1/packages/commit-helper/versions/1.3.0/download",
"sha256": "3b5d5c3712955042212316173ccf37be800ea6b9b4d2f3e6b3e1c3d2a1b0c9d8",
"size": 742
},
"installTargets": {
"claude-code": {
"mode": "directory",
"project": ".claude/skills/commit-helper",
"global": ".claude/skills/commit-helper"
},
"codex": {
"mode": "directory",
"project": ".agents/skills/commit-helper",
"global": ".codex/skills/commit-helper"
},
"cursor": {
"mode": "skill-to-mdc",
"project": ".cursor/rules",
"global": null
}
},
"summary": "All 8 checks passed.",
"archived": false,
"deprecated": null,
"premium": null,
"moderation": null,
"report": {
"verifierVersion": "1.1.0",
"status": "verified",
"riskLevel": "none",
"checkedAt": "2026-10-16T17:54:30.832Z",
"summary": "All 8 checks passed.",
"checks": [
{
"id": "files.limits",
"category": "structure",
"level": "pass",
"message": "1 files, 167 B — within size limits."
},
{
"id": "skill.frontmatter",
"category": "structure",
"level": "pass",
"message": "SKILL.md has valid YAML frontmatter.",
"file": "SKILL.md"
},
{
"id": "skill.name",
"category": "structure",
"level": "pass",
"message": "`name` is valid (\"commit-helper\").",
"file": "SKILL.md"
},
{
"id": "skill.description",
"category": "structure",
"level": "pass",
"message": "`description` is present.",
"file": "SKILL.md"
},
{
"id": "compat.claude-code",
"category": "compatibility",
"level": "pass",
"message": "Installs correctly for Claude Code → .claude/skills/commit-helper."
},
{
"id": "compat.codex",
"category": "compatibility",
"level": "pass",
"message": "Installs correctly for OpenAI Codex → .agents/skills/commit-helper."
},
{
"id": "compat.cursor",
"category": "compatibility",
"level": "pass",
"message": "Installs correctly for Cursor → .cursor/rules."
},
{
"id": "security.scan",
"category": "security",
"level": "pass",
"message": "Security scan found no suspicious patterns."
}
],
"findings": [],
"compatibility": [
{
"tool": "claude-code",
"ok": true,
"target": ".claude/skills/commit-helper",
"issues": []
},
{
"tool": "codex",
"ok": true,
"target": ".agents/skills/commit-helper",
"issues": []
},
{
"tool": "cursor",
"ok": true,
"target": ".cursor/rules",
"issues": []
}
],
"files": [
{
"path": "SKILL.md",
"size": 167,
"sha256": "84108285e5634549ce6cfeabf9ccc548b88b0ef4f9d3d721e847aa3132197e64"
}
],
"stats": {
"fileCount": 1,
"totalSize": 167
},
"metadata": {
"name": "commit-helper",
"description": "Writes Conventional Commit messages from the staged diff."
},
"mainFile": "SKILL.md",
"diff": null,
"allowlist": null
}
}403 The version was blocked by the moderators and cannot be installed.
{
"error": "commit-helper@1.2.0 was blocked by the Denofy moderators and cannot be installed."
}404 The package or a matching version does not exist, is private or was hidden by the moderators.
{
"error": "Package \"nope\" not found"
}Download the tarball#
GET/api/v1/packages/{name}/versions/{version}/download
Streams the immutable .tgz of a version. Compare its sha256 with tarball.sha256 from the version endpoint (the CLI does this automatically). Each download is counted unless count=0 is passed. Premium packages (premium is set) need a license: send Authorization: Bearer <token> (from denofy login); the response is then a redirect to a signed link that is valid for 60 seconds.
| Parameter | In | Type | Description |
|---|---|---|---|
namerequired | path | string | Package name (slug), e.g. commit-helper. |
versionrequired | path | string | Exact version (1.2.0), latest, or a URL-encoded semver range (%5E1.2.0 for ^1.2.0). Ranges resolve to the highest matching version that is not yanked or blocked. |
count | query | "0" | "1" | 0: do not count this download (mirrors, tests). |
| Response header | Description |
|---|---|
x-denofy-version | Resolved version |
x-denofy-sha256 | sha256 of the tarball (hex) |
x-denofy-commit | Git commit the version was built from |
x-denofy-status | Verification status |
cache-control | public, max-age=31536000, immutable |
curl -LO https://denofy.com/api/v1/packages/commit-helper/versions/1.3.0/download200 The tarball.application/gzip
302 Premium package with a valid license: redirect to a short-lived signed download link.
402 Premium package without a valid license (or without a token).
{
"error": "commit-helper is a premium package and you have no valid license.",
"code": "license_required",
"purchaseUrl": "https://denofy.com/p/commit-helper#premium"
}403 The version was blocked by the moderators.
{
"error": "commit-helper@1.2.0 was blocked by the Denofy moderators and cannot be installed."
}404 The package or version does not exist, is private or was hidden by the moderators.
{
"error": "Package \"nope\" not found"
}410 The tarball is missing from storage.
{
"error": "Tarball is missing from storage"
}README badge#
GET/api/v1/packages/{name}/badgenot rate limited
SVG badge with the verification status and latest version. Not rate limited (GitHub fetches badges through a few proxy IPs). Cached for 5 minutes; unknown packages get a grey "not found" badge with status 404, cached for 1 minute.
| Parameter | In | Type | Description |
|---|---|---|---|
namerequired | path | string | Package name (slug), e.g. commit-helper. |
curl https://denofy.com/api/v1/packages/commit-helper/badge200 SVG badge.image/svg+xml
404 SVG badge "not found".image/svg+xml
Errors#
Errors use a JSON body with a human-readable message:
{ "error": "Package \"nope\" not found" }| Status | Meaning |
|---|---|
400 | Malformed request. |
402 | Premium package: a license is needed (see purchaseUrl). |
403 | The version was blocked by the Denofy moderators. |
404 | Package or version not found (also private and hidden packages). |
410 | The tarball is gone from storage (please report it). |
429 | Rate limit exceeded (300 requests per 60 s and IP by default). Wait for Retry-After seconds. |
500 | Unexpected server error. |
503 | Temporarily unavailable (database down or maintenance). |
Rate limits#
All /api/v1 endpoints except the badge are limited to 300 requests per 60 seconds and IP by default (fixed
windows; a self-hosted instance can change this with RATE_LIMIT_API_IP). Every response carries
the current state:
| Header | Description |
|---|---|
x-ratelimit-limit | Requests allowed in the current window. |
x-ratelimit-remaining | Requests left in the current window. |
retry-after | Only on 429: seconds until the window resets. |
When the limit is exceeded the API answers 429:
{ "error": "Too many requests. Try again in 42 seconds.", "retryAfter": 42 }Wait for Retry-After seconds before the next request. The CLI waits once automatically for short
limits (up to 10 seconds) and otherwise prints how long to wait. Other limits of the platform:
| Limit | Default |
|---|---|
| REST API (/api/v1, CLI) per IP | 300 per 1 min |
| Sign-in attempts per IP | 10 per 10 min |
| New packages per user | 10 per 1 h |
| New packages per IP | 20 per 1 h |
| Syncs and re-verifications per user | 30 per 1 h |
| Syncs per package (manual and webhook) | 10 per 1 h |
| Reviews per user | 10 per 1 h |
| Reviews per IP | 30 per 1 h |
| Reports per user | 10 per 1 h |
| Reports per IP | 20 per 1 h |
| Follows and unfollows per user | 60 per 1 h |
| Favourites per user | 60 per 1 h |
| Collection changes per user | 30 per 1 h |
| Maintainer invitations per user | 20 per 1 h |
| Checkouts started per user | 10 per 1 h |
| Checkouts started per IP | 20 per 1 h |
| Verification e-mails per user | 5 per 1 h |
| Data exports per user | 5 per 1 h |
| Image uploads per user | 30 per 1 h |
| CLI sign-in requests per IP | 20 per 1 h |
Versioning of the API#
/api/v1 only gets backwards-compatible changes: new fields and new endpoints. Clients should
ignore unknown fields. Breaking changes would get a new prefix.
Example: install without the CLI#
# 1. resolve the version and read the tarball hash
curl -s https://denofy.com/api/v1/packages/commit-helper/versions/latest \
| jq -r '.tarball.url, .tarball.sha256'
# 2. download and verify
curl -sLo pkg.tgz https://denofy.com/api/v1/packages/commit-helper/versions/1.3.0/download
echo "<sha256> pkg.tgz" | sha256sum -c -
# 3. extract into the tool folder (the tarball has one top-level folder)
mkdir -p .claude/skills/commit-helper
tar -xzf pkg.tgz -C .claude/skills/commit-helper --strip-components=1