API
Distribution operations
Archives and hosted marketplaces for tools that do not speak this API (unzip, Claude Code plugin marketplaces).
Archives and hosted marketplaces for tools that do not speak this API (unzip, Claude Code plugin marketplaces). Paths are relative to https://reuseforge.com/api/v1.
| Operation | Method | Path |
|---|---|---|
| Download a version as a ZIP archive | GET | /items/{owner}/{slug}/versions/{version}/archive.zip |
| Claude Code plugin marketplace of an owner | GET | /marketplaces/{owner}.json |
Download a version as a ZIP archive
GET /items/{owner}/{slug}/versions/{version}/archive.zip Enabled on reuseforge.com
All files of a version as one deterministic, store-only (uncompressed) ZIP, for tools that understand archives but not this API (unzip, Claude Code archive plugin sources, CI caches). The same version always yields byte-identical output, so its SHA-256 (X-Content-SHA256, also ItemVersion.archive_sha256 once known) can be pinned: entries are sorted by UTF-8 path bytes, timestamps are fixed to 1980-01-01 00:00:00, permissions are 0644 (0755 for files the publish manifest lists in executable), names are flagged UTF-8, there are no directory entries, extra fields or data descriptors.
layout selects the variant. plain (default) holds the published files at their paths. claude-plugin wraps a skill, prompt or mcp_server item into a Claude Code plugin (.claude-plugin/plugin.json, skills/<slug>/..., commands/<slug>.md or .mcp.json); other kinds answer 400. Each layout has its own digest.
Access is exactly that of get_version_bundle: free versions are open to everyone, paid versions return 402 payment_required with an order_hint until you are entitled. A version that has any non-preview file is never served partially: no entitlement means 402, not a smaller archive. A quarantined version answers 403 version_quarantined, as for get_version_bundle, whatever If-None-Match says. latest resolves like get_version. Content-Length and ETag (the quoted SHA-256) are exact, so If-None-Match revalidation answers 304. A yanked version still downloads and carries X-RF-Yanked: 1. A retracted free item answers 410; a retracted paid item stays downloadable for the accounts that bought it.
The archive is built from the version's files and its stored, validated publish manifest only: a file named reuseforge.json is ordinary content and never configures the plugin, and a version published without a manifest has no mcp section, so an mcp_server version cannot take the claude-plugin layout. The claude-plugin layout is refused (400) once the item's kind differs from the kind in the stored manifest. Names that would overwrite each other on a case-insensitive or normalising filesystem (SKILL.md and Skill.md, NFC and NFD spellings, a file that is also a directory) make the version unarchivable: 400, without a layout pointer.
| Property | Value |
|---|---|
| Operation ID | get_version_archive |
| Authentication | None required. A token raises your rate limits and shows your private items. |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Caching | Sends an ETag; repeat with If-None-Match to get 304 Not Modified |
| Python SDK | client.get_version_archive(...) |
| JavaScript SDK | client.getVersionArchive(...) |
| CLI | rf versions download --format zip |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
slug | path | string | yes | Item slug, unique per owner. |
version | path | string | yes | An exact semantic version or latest. |
layout | query | string: plain, claude-plugin | no | plain (default): the published files as they are. claude-plugin: a Claude Code plugin wrapping a skill, prompt or mcp_server item. Default "plain". |
If-None-Match | header | string | no | ETag from an earlier response; a match returns 304 without a body. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The archive. | application/zip: string (binary) |
| 304 Not Modified | Not modified. Keeps ETag and X-Content-SHA256. | no body |
| 400 | The request is malformed (bad JSON, unknown or mistyped parameter, invalid cursor). | Problem. Codes: invalid_request, invalid_json, invalid_parameter, invalid_cursor, idempotency_key_required |
| 402 | The item is paid and the caller is not entitled. order_hint says exactly what to order. | Problem. Codes: payment_required |
| 403 | Authenticated, but not allowed. Most often a missing scope (insufficient_scope, with required_scopes), an exceeded spend limit, or a role on the owning account that is too low. Also version_quarantined (the security scan or a moderator blocked the version you asked for) and review_removed (a moderator removed your review). | Problem. Codes: insufficient_scope, forbidden, account_suspended, email_unverified, spend_limit_exceeded, purchase_required, quota_exceeded, version_quarantined, review_removed |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 410 | The resource existed and is permanently unavailable (retracted item, expired plan). | Problem. Codes: gone, plan_expired |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl -o invoice-extractor-1.4.0.zip 'https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/1.4.0/archive.zip'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
archive = client.get_version_archive("acme/invoice-extractor", "1.4.0")
print(len(archive), "bytes")JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const archive = await client.getVersionArchive("acme/invoice-extractor", "1.4.0");
console.log(archive.byteLength, 'bytes');CLI
rf versions download --format zip acme/invoice-extractor 1.4.0
# rf versions download <item> [version] [options]
# all options: rf versions download --helpResponse 200: the body is application/zip, not JSON.
Claude Code plugin marketplace of an owner
GET /marketplaces/{owner}.json Enabled on reuseforge.com
A hosted Claude Code plugin marketplace for one account. Add it with /plugin marketplace add https://reuseforge.com/api/v1/marketplaces/<owner>.json, then /plugin install <slug>@reuseforge-<owner>.
It lists the owner's public, free, active items of kind skill, prompt or mcp_server that have a latest non-yanked, non-prerelease, non-quarantined version. Retracted, archived, draft, private, unlisted and paid items never appear, and neither does a quarantined version (the entry falls back to the next stable release). Each entry's source is {source: archive, url, sha256}: the claude-plugin layout of get_version_archive for that exact version, pinned by its SHA-256, so Claude Code refuses a download that differs. An owner without matching items yields an empty plugins list; a handle that does not exist is 404. The marketplace name is reuseforge-<owner>. Entries whose plugin name would read as one of Anthropic's own are left out. A cold digest cache is filled for at most 8 versions per request; entries not pinned yet are omitted and X-ReuseForge-Marketplace-Pending reports how many, with a short cache lifetime so the list completes on the next fetch (a 304 repeats the header). A version whose build started and never finished is skipped for 15 minutes and given up after 3 attempts; a version that cannot be packaged is left out. At most 100 plugins are listed, most downloaded first; when more items qualify, X-ReuseForge-Marketplace-Truncated: 100 says so. Entries carry keywords (the item's tags, also repeated as tags).
| Property | Value |
|---|---|
| Operation ID | get_marketplace |
| Authentication | None required. A token raises your rate limits and shows your private items. |
| Rate tier | read: 60/min per IP anonymous, 600/min per token with a token |
| Caching | Sends an ETag; repeat with If-None-Match to get 304 Not Modified |
| Python SDK | client.get_marketplace(...) |
| JavaScript SDK | client.getMarketplace(...) |
| CLI | rf marketplaces get |
| MCP tool | not exposed as a tool |
| Deployments | reuseforge.com: enabled. Python node: not implemented (answers 501). |
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
owner | path | string | yes | Handle of the owning user or organization. |
If-None-Match | header | string | no | ETag from an earlier response; a match returns 304 without a body. |
Responses
| Status | Meaning | Body |
|---|---|---|
| 200 OK | The marketplace document (the file Claude Code reads as marketplace.json). | application/json: ClaudeMarketplace |
| 304 Not Modified | Not modified. Keeps ETag and X-ReuseForge-Marketplace-Pending. | no body |
| 404 | No such resource, or it is private and the caller may not know it exists. | Problem. Codes: not_found |
| 429 | Rate limit for the operation's tier exceeded. Wait Retry-After seconds. | Problem. Codes: rate_limited |
Example
curl
curl 'https://reuseforge.com/api/v1/marketplaces/acme.json'Python
import reuseforge
client = reuseforge.Client() # token from REUSEFORGE_TOKEN, or the file `rf login` wrote
result = client.get_marketplace("acme")
print(result["name"])JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge({ token: process.env.REUSEFORGE_TOKEN });
const result = await client.getMarketplace("acme");
console.log(result.name);CLI
rf marketplaces get acme
# rf marketplaces get <owner>
# all options: rf marketplaces get --helpResponse 200 OK:
{
"name": "reuseforge-acme",
"description": "Free skills, prompts and MCP servers published by acme on ReuseForge.",
"owner": {
"name": "Acme Inc.",
"url": "https://reuseforge.com/app/acme"
},
"plugins": [
{
"name": "invoice-extractor",
"source": {
"source": "archive",
"url": "https://reuseforge.com/api/v1/items/acme/invoice-extractor/versions/1.4.0/archive.zip?layout=claude-plugin",
"sha256": "6bfa50e3d2e00c052b46abe51fff89346ac803e45771f76dcf6df1ab74cca5e1"
},
"description": "Extract line items and totals from invoice PDFs.",
"version": "1.4.0",
"category": "skill",
"author": {
"name": "Acme Inc."
},
"homepage": "https://reuseforge.com/app/acme/invoice-extractor",
"license": "MIT",
"keywords": [
"invoices",
"pdf"
]
}
]
}