Switchfirm API

JSON endpoints served by /api.php. CORS enabled (GET), ETag and caching provided out of the box.

Base URL & Parameters

Base: /api.php

Required: actionlatest, list, search, get, manifest, health

Optional:

  • pretty — pretty-printed JSON (no value needed)
  • data — override catalog path (safe, same-dir/subdir only). Default: firmwares.json
  • manifests — override manifests folder (safe, same-dir/subdir). Default: manifests/

Content-Type: application/json; charset=utf-8. Cache: Cache-Control: public, max-age=60 (configurable in PHP). CORS: Access-Control-Allow-Origin: *.

GET /api.php?action=health health

Basic health check.

{
  "status": "ok",
  "time": "2025-10-11T00:00:00Z"
}

200 on success.

GET /api.php?action=latest newest

Newest firmware (sorted by version desc; hyphen suffixes like 1.0.0-7 supported).

{
  "name": "Firmware 20.5.0",
  "version": "20.5.0",
  "file": "download/global/Firmware 20.5.0.zip",
  "md5": "4ea8aeb49ab60e2c1a3d08949f979600"
}

GET /api.php?action=list catalog

Entire catalog (optionally paginated).

  • limit — number of items to return
  • offset — start index
{
  "count": 87,
  "offset": 0,
  "limit": 87,
  "items": [
    { "name": "Firmware 20.5.0", "file": "download/global/Firmware 20.5.0.zip", "md5": "..." },
    ...
  ]
}

GET /api.php?action=search&q=<term> search

Case-insensitive search over name and md5. Returns newest → oldest.

{
  "query": "20.5",
  "count": 1,
  "items": [{ "name": "Firmware 20.5.0", "file": "...", "md5": "..." }]
}

GET /api.php?action=get&name=<firmware-name> lookup

Lookup one item by exact name; falls back to case-insensitive and fuzzy contains.

{
  "name": "Firmware 20.4.0",
  "file": "download/global/Firmware 20.4.0.zip",
  "md5": "7dabad47ab164a6a41952ce75bb0d24b"
}

Errors: 400 (missing name), 404 (not found).

GET /api.php?action=manifest&name=<firmware-name> per-version

Returns the manifest JSON from /manifests/<name>.json. Uses catalog to resolve canonical casing.

{
  "name": "Firmware 20.5.0",
  "file_count": 123,
  "files": [
    { "path": "nx/system/file.bin", "sha256": "...", "md5": "..." },
    ...
  ]
}

Errors: 400 (missing name), 404 (manifest not found), 500 (invalid JSON / read error).

ETag, Caching & Conditional Requests

  • Responses include ETag; clients may send If-None-Match to receive 304 Not Modified.
  • Default Cache-Control: public, max-age=60 (change $DEFAULT_MAX_AGE_SEC in api.php).
  • list/search ETag is derived from the catalog file’s ino:size:mtime (or content hash).

CORS & Methods

  • Allowed: GET, OPTIONS
  • CORS: Access-Control-Allow-Origin: *, Vary: Origin
  • Preflight: API returns 204 for OPTIONS.

Quick Examples

Fetch newest (browser):

fetch('/api.php?action=latest')
  .then(r => r.json())
  .then(console.log);

Search (curl):

curl -s "https://switchfirm.org/api.php?action=search&q=20.5"

Get manifest (Node + jq):

curl -s "https://switchfirm.org/api.php?action=manifest&name=Firmware%2020.5.0" | jq .file_count