Plugins API
Read the plugin index, releases, and download URLs over HTTP.
The hub exposes a read-only HTTP API over the same data the website renders. It exists so launchers, package managers, dashboards, and CI jobs can find plugins without scraping the pages.
Base URL:
https://hub.getaltay.orgNo key, no registration, no Authorization header. Every endpoint on this page answers anonymous
requests and sends Access-Control-Allow-Origin: *, so browser code can call it directly.
Only published plugins appear. Anything still waiting for review, or rejected, is invisible here.
Conventions
Responses are JSON with Content-Type: application/json. Field names are snake_case. Timestamps
are ISO 8601 in UTC. Every URL in a response is absolute, so you can follow it without rebuilding
it yourself.
Responses carry Cache-Control: public, max-age=60. Cache them. Polling this API in a tight loop
gets you a minute-old answer anyway.
Errors use the HTTP status plus a body:
{ "error": "Unknown sort \"bogus\". Use updated, downloads, stars, name." }| Status | Meaning |
|---|---|
200 | Fine |
400 | A query parameter was malformed. The message says which |
404 | No plugin by that name |
503 | The index is temporarily unreachable. Retry with a backoff |
List plugins
GET /api/hub/pluginsQuery parameters
| Parameter | Type | Default | Notes |
|---|---|---|---|
q | string | none | Matches name, description, and keywords. Case-insensitive, 64 characters max |
api | string | none | Keep only plugins whose newest release targets this API version, exactly MAJOR.MINOR.PATCH |
sort | string | updated | One of updated, downloads, stars, name |
limit | integer | 30 | Clamped to 100 |
offset | integer | 0 | For paging |
Response
{
"total": 8,
"limit": 2,
"offset": 0,
"plugins": [
{
"name": "Customies",
"slug": "customies",
"description": "A PocketMine-MP plugin that implements support for custom blocks, items and entities.",
"keywords": ["custom", "items", "blocks"],
"author": {
"login": "xRookieFight",
"url": "https://hub.getaltay.org/u/xRookieFight"
},
"repository": {
"full_name": "altayofficial/Customies",
"url": "https://github.com/altayofficial/Customies"
},
"latest": {
"version": "1.5.0",
"api": ["5.44.0"],
"download_url": "https://hub.getaltay.org/download/customies/1.5.0"
},
"stats": { "downloads": 3, "stars": 0 },
"icon_url": "https://hub.getaltay.org/icon/customies?v=1789852301219",
"url": "https://hub.getaltay.org/p/customies",
"created_at": "2026-09-19T21:11:41.219Z",
"updated_at": "2026-09-19T21:11:41.219Z"
}
]
}total counts everything matching the filter, not the page you got back. Page with offset until
offset + limit >= total.
latest is null for a plugin whose releases were all withdrawn. Check it before reading
latest.version.
slug is the lowercase name and the id used in every other URL.
Get one plugin
GET /api/hub/plugins/{slug}Same object as the list, plus the full description and every published release, newest first.
{
"name": "Customies",
"slug": "customies",
"long_description": "# Customies\r\n\r\nA PocketMine-MP plugin that...",
"releases": [
{
"version": "1.5.0",
"api": ["5.44.0"],
"ref": "v1.5.0",
"size_bytes": 105061,
"sha256": "f5dfe0d6276ad5b905ab84eecd5b901aab5905dacde79d6ba988479b57bc26d9",
"download_url": "https://hub.getaltay.org/download/customies/1.5.0",
"published_at": "2026-09-19T21:11:41.219Z"
}
]
}long_description is Markdown, and may contain HTML, written by the plugin author. The hub
sanitises it against an allowlist before rendering, but the API hands you the original. Treat it as
untrusted: escape it, or run it through your own sanitiser before putting it in a page.
ref is the git tag or commit the phar was built from, in the repository named by repository.
Download a release
GET /download/{slug}/{version}Returns the phar with Content-Type: application/octet-stream. This is a normal file download, not
JSON, and it increments the plugin's download counter. Follow redirects.
Verify what you fetched against the sha256 from the plugin endpoint before loading it on a
server:
curl -fLo Customies.phar https://hub.getaltay.org/download/customies/1.5.0
sha256sum Customies.pharPlugin icon
GET /icon/{slug}Returns the author's icon as a PNG, at most 256x256. Plugins without one get a generated placeholder instead, served as image/svg+xml, so branch on Content-Type rather than assuming PNG.
Icons live at a stable URL, so append the plugin's updated_at as a cache buster when you want the
current one. That is exactly what icon_url in the API response already does.
Examples
# Everything that runs on API 5.0.0, most downloaded first
curl -s "https://hub.getaltay.org/api/hub/plugins?api=5.0.0&sort=downloads" \
| jq -r '.plugins[] | "\(.name) \(.latest.version) - \(.stats.downloads) downloads"'Paging through everything
offset=0
while :; do
page=$(curl -s "https://hub.getaltay.org/api/hub/plugins?limit=100&offset=$offset")
echo "$page" | jq -r '.plugins[].slug'
total=$(echo "$page" | jq '.total')
offset=$((offset + 100))
[ "$offset" -ge "$total" ] && break
doneChecking for updates
Store the version you installed and compare it against latest.version. Do not compare
updated_at: it also moves when an author edits a description or swaps an icon, which is not a new
build.
installed="1.4.0"
latest=$(curl -s https://hub.getaltay.org/api/hub/plugins/customies | jq -r '.latest.version')
[ "$installed" != "$latest" ] && echo "Customies $latest is out"Limits and etiquette
There is no per-key quota, because there are no keys. Instead:
- Responses are cacheable for 60 seconds. Respect that rather than polling every second.
limitabove 100 is silently clamped to 100.- Identify your tool in the
User-Agentheader. If something misbehaves, that header is what tells us who to contact instead of blocking a whole network. - Mirror the index if you are serving many users. One fetch per minute for your service beats one fetch per user.
What is not here
Publishing, editing, starring, and commenting all require a signed-in GitHub session and are not reachable with a token. There is no write API. Submit plugins through the hub.
Nobody audits plugin source on your behalf. The hub builds each phar from a public repository at a tag the author picked, and a moderator checks the submission, but a passing review is not a promise that the code is safe. Read the source before running it on a server you care about.