Skip to content
GPU Ninja

Caching, redirects and errors

Last updated

Response headers#

Every API response is a static file served by GPU Ninja's CDN. A successful response looks like this:

HTTP/2 200
content-type: application/json
cache-control: public, max-age=300
access-control-allow-origin: *
etag: "…"
strict-transport-security: max-age=63072000; includeSubDomains
x-content-type-options: nosniff
referrer-policy: strict-origin-when-cross-origin
content-security-policy: default-src 'self'; …
x-frame-options: DENY

The Content-Security-Policy and X-Frame-Options headers are on every response, because one rule covers the whole site; they are meant for the web pages, and clients ignore them on JSON and text.

FilesCache-ControlContent-Type
Everything under /api/v1/public, max-age=300application/json
Every .txt file, including /llms.txtpublic, max-age=300text/plain; charset=utf-8
Feeds and /sitemap.xmlpublic, max-age=300the feed's own type
Web pagespublic, max-age=0, must-revalidatetext/html

The API never sets cookies, and GPU Ninja sets no Vary header. There is no CDN-Cache-Control header and no cache rule in front of the site, so a deploy is visible as soon as your copy's five minutes are up.

Caching and freshness#

A response may be up to five minutes older than the latest deploy, because of max-age=300. Every payload carries absolute timestamps (asOf, retrievedAt, lastVerifiedAt, mergedAt), so a cached copy is never wrong about how old its data is, only about whether something newer has been published since.

Revalidating with the ETag#

Each response has an ETag. Send it back in If-None-Match; if the file hasn't changed, you get 304 Not Modified with no body:

curl -sI https://gpuninja.com/api/v1/index
curl -sI -H 'If-None-Match: "<etag from the first response>"' https://gpuninja.com/api/v1/index
HTTP/2 304
cache-control: public, max-age=300
  • The ETag is derived from the file's content, so an unchanged file keeps its ETag across deploys and still revalidates with a 304 after one.
  • The extensionless URL and the .json URL share one ETag.
  • The ETag may arrive in weak form (W/"…"). Send it back as you received it; either form is accepted.
  • Send one ETag per request. Don't rely on a list of tags or on If-None-Match: *.
  • A 304 carries the same headers as the 200 would, including Cache-Control, so it renews your copy for another five minutes.
  • There is no Last-Modified header, so use If-None-Match rather than If-Modified-Since.

CORS#

Every response, including redirects and 404s, has Access-Control-Allow-Origin: *. Nothing on the site is authenticated and no credentials are involved, so any origin can read the API from the browser.

Important

Only CORS simple requests work. The CDN answers OPTIONS with 405, so any request that needs a preflight fails. From browser code, make a plain GET with no custom headers. Setting If-None-Match or any other non-simple header yourself triggers a preflight; the browser's own HTTP cache revalidates without one, so let it do that.

Server-side clients, such as curl, Node or a Worker, aren't subject to CORS and can send any headers.

Redirects#

RequestResponse
An old slug after a rename, such as /api/v1/boards/{old-slug}301 to the new slug's URL. The .json form, the compatibility files, and the web page and its .txt redirect the same way. The query string is kept
An API path with a trailing slash, such as /api/v1/boards/{slug}/308 to the same path without the slash
A web page's .html file, such as /docs.html308 to the extensionless page URL
Any path on www.gpuninja.com or gpuninja.pages.dev301 to the same path on gpuninja.com, query string kept

A rename redirect always points at the final URL, never at another redirect. A redirect is cached for the same time as its source path: five minutes under /api/ and for .txt files, and revalidated on every request for web pages. Redirects are never cached indefinitely, so one can be withdrawn.

Follow redirects in your client (curl -L). If you store slugs, update a stored slug when you see a 301 for it; the changes feed also reports entities by their current slug.

Errors#

404 Not Found#

A path with no file returns 404 with Cache-Control: no-store and Access-Control-Allow-Origin: *. The body is the website's HTML "Page not found" page, even under /api/, so rely on the status code and don't parse the body. Under a .txt path the same HTML body is labelled text/plain; charset=utf-8.

Common causes:

  • Wrong case. Paths are lowercase and case-sensitive.
  • A page past the end. Read pageCount from page 1; later pages don't exist. There is also no /page/1: page 1 is the collection URL.
  • A platform or benchmark page. Platforms and benchmarks are in the API but have no web page and no .txt; their links.page and links.text are null.
  • A removed entity. Discontinued hardware is never removed, but a record entered in error can be. Its last change appears in the changes feed with op: "removed". For up to about a week after a deploy, the hosting platform may still serve a removed file with a 200 from its own cache, so treat the changes feed as the record of what was removed.

405 Method Not Allowed#

Only GET and HEAD are supported. POST, OPTIONS and every other method return 405, with Access-Control-Allow-Origin: * and the path's usual Cache-Control. Redirects are checked first, so a request of any method to a renamed slug or a path with a trailing slash gets its 301 or 308 instead.

5xx#

A server error comes from the hosting platform before any of GPU Ninja's headers are applied, so it carries no CORS header and no cache policy. Retry later with backoff.

Query strings#

Query parameters are ignored everywhere on the API. ?page=2 returns page 1, and filter or sort parameters do nothing. Page through collections by path, and filter the search index on your side or ask through the MCP servers.

Being a good citizen#

There is no API key and no rate limit on gpuninja.com, and every response is cacheable. In return, please:

  • cache what you download and revalidate with If-None-Match rather than re-downloading;
  • poll no more often than every five minutes, since nothing changes faster than that;
  • read the search index and the changes feed instead of crawling every entity on a schedule.