Skip to content
GPU Ninja

Public API

Last updated

Overview#

GPU Ninja publishes its whole catalogue as a read-only JSON API. Every response is a static file, rebuilt and uploaded on each deploy, so there is no server to query and nothing to sign up for.

  • Base URL: https://gpuninja.com/api/v1/
  • Format: JSON only, served as application/json.
  • Authentication: none. There is no API key and no account.
  • Rate limit: none. Please be reasonable: cache responses, revalidate with the ETag, and download the compact search index rather than crawling every entity.
  • Methods: GET and HEAD. Every other method, including OPTIONS, returns 405.
  • Licence: the data is CC BY 4.0. Use it for anything, including commercial apps, and credit GPU Ninja. The OpenAPI document is Apache 2.0.

Sample data for now. Until the research pipeline publishes the real catalogue, the API serves GPU Ninja's small development dataset: a few chips, graphics cards, cases, models, motherboards and risers, with sample offers. Its prices and stock are illustrative, not quotes. The URLs, shapes and rules on this page are the real contract and won't change when the real data arrives.

There is no server-side filtering. Filter on your side over the compact search index (see Search), or ask through the MCP servers, which do it for AI assistants. The buyer's guide does the same on the website. All of them use one open-source search library that you can run yourself.

Quick start#

Fetch the first page of graphics cards:

curl -s https://gpuninja.com/api/v1/boards

Fetch one card, then the cases it fits and the AI models it can run:

curl -s https://gpuninja.com/api/v1/boards/asus-rog-strix-4090
curl -s https://gpuninja.com/api/v1/boards/asus-rog-strix-4090/fits
curl -s https://gpuninja.com/api/v1/boards/asus-rog-strix-4090/can-run

Download the compact search index and the last 30 days of changes:

curl -s https://gpuninja.com/api/v1/index
curl -s https://gpuninja.com/api/v1/changes

Add -L if you want curl to follow redirects, which matters when a slug has been renamed.

Endpoints#

{collection} is one of chips, boards, chassis, models, platforms, benchmarks, motherboards, risers or sockets. {slug} is a lowercase kebab-case identifier of at most 80 characters, such as asus-rog-strix-4090; page, index and 404 are never slugs. {n} is a page number of 2 or more, and {month} a calendar month in UTC, YYYY-MM.

PathReturns
/api/v1/{collection}Page 1 of a collection, 50 items per page
/api/v1/{collection}/page/{n}Page n of a collection
/api/v1/{collection}/{slug}One entity in full
/api/v1/boards/{slug}/fitsEvery case, with a fit verdict for this card
/api/v1/chassis/{slug}/fitsEvery graphics card, with a fit verdict for this case
/api/v1/boards/{slug}/can-runAI models that fit in this card's VRAM
/api/v1/models/{slug}/runs-onGraphics cards with enough VRAM for this model
/api/v1/indexThe compact search index of chips, boards, chassis, models, motherboards and risers
/api/v1/changesEvery data change merged in the last 30 days, and a list of the monthly archives
/api/v1/changes/{month}Every data change merged in that month, for each month that has one
/api/v1/openapi.jsonThe OpenAPI 3.1.1 description of every path and schema (see OpenAPI and generated clients)

Field-by-field detail is in the pages of this section: Collections and entities, Compatibility, Search index, Changes feed and Caching, redirects and errors.

URL rules#

  • Two forms, one resource. Every resource answers both without an extension and with .json: /api/v1/boards/asus-rog-strix-4090 and /api/v1/boards/asus-rog-strix-4090.json return the same bytes and the same ETag. The OpenAPI document is the exception: it is only at /api/v1/openapi.json.
  • Lowercase and case-sensitive. Paths are lowercase. /API/V1/BOARDS is a 404; nothing folds case for you.
  • No trailing slash. An API path with a trailing slash redirects with 308 to the same path without it.
  • Query parameters are ignored. /api/v1/boards?page=2 and /api/v1/boards?vram=16 return exactly what /api/v1/boards returns. There is no server-side filtering, sorting or field selection.
  • Pagination is by path. Page 1 is the collection URL itself, and later pages are /page/2, /page/3 and so on, 50 items per page. There is no /page/1, and pages beyond pageCount don't exist. Each page links to first, last, prev and next.
  • Renames are 301s. If an entity's slug changes, every old URL redirects permanently to the new one: the API document and its compatibility files (with or without .json), and the web page and its .txt. The query string is kept. Each redirect points straight at the final URL, so you never follow a chain.
  • 404s are HTML. A path that doesn't exist returns status 404 with the site's HTML error page as the body, even under /api/. Check the status code; don't try to parse the body.

Response conventions#

Every JSON file except openapi.json carries the same envelope: apiVersion (always "v1"), kind (what the document is, such as "board", "collection" or "board-fits"), asOf, and links.

  • Nothing is optional. Every property is always present. An unknown value is null, never a missing key, an empty string or 0; a list with nothing in it is [].
  • Property names are camelCase, and enumerated values are kebab-case (does-not-fit, board-can-run).
  • Units are in the field name. lengthMm is millimetres, tdpW watts, vramGb gigabytes, factoryBoostClockMhz megahertz, memoryBusWidthBits bits, parametersB billions of parameters, contextLength tokens, memoryBandwidthGbps decimal gigabytes per second, fp32Tflops trillions of floating-point operations per second and int8Tops trillions of integer operations per second. slotWidth and maxGpuSlots are expansion slots and may be fractional, such as 3.5.
  • Timestamps are absolute. Instants are UTC at whole-second precision with a Z suffix, such as 2026-10-04T06:12:00Z. Calendar dates are YYYY-MM-DD. Nothing in the API is relative ("3 days ago"); work that out yourself at the moment you display it.
  • Money. lowestPriceUsd and launchMsrpUsd are decimal US dollars. An offer's price is a decimal amount in that offer's own currency. Amounts have at most two decimal places.
  • Links are absolute. links.self is the extensionless API URL. links.page and links.text are the matching web page and its plain-text companion, or null for platforms, benchmarks, motherboards, risers and sockets, which have no pages.

asOf and provenance#

asOf is the most recent time any value in the file was retrieved or verified. Each entity that appears in the file contributes the timestamps of its own values and offers, and nothing else. It is never the build time, so a cached copy is honest about how old its data is. It is null when nothing in the file has a timestamp. On /api/v1/changes it is the latest merge time in the window instead, and on a monthly archive the latest merge time in that month. Freshness for individual fields lives in each entity's provenance map, which names the source URL, when the value was retrieved, when a source last confirmed it, and its verification status. See Collections and entities and Data.

Discontinued and frozen records#

Chips, boards, chassis, motherboards and risers have a status of active or discontinued. A discontinued record is a frozen snapshot: its specs, last recorded offers and benchmark results are preserved exactly as they were and never updated again, except by an explicit correction. A discontinued motherboard is frozen with its PCIe slots. Discontinued records stay in every collection, keep their URLs, and still get fit and can-run answers against newly added hardware and models. The one exception is a discontinued board, which is API only: it keeps its document at /api/v1/boards/{slug}, its place in the complete list and the search index, and it still appears in a case's fits and a model's runs-on, but it has no site page, no .txt companion and no fits or can-run document of its own. Its links.page and links.text are null, and so are compatibility.fits and compatibility.canRun. A discontinued board's or case's inStock is false and its lowestPriceUsd is null, because a frozen price is history, not a current price. Models, platforms, benchmarks and sockets are never discontinued and never freeze.

Motherboards, risers and sockets#

Three collections exist for the Solution Builder and are API only: /api/v1/motherboards, /api/v1/risers and /api/v1/sockets, with one document per entity at /api/v1/motherboards/{slug}, /api/v1/risers/{slug} and /api/v1/sockets/{slug}. They are JSON only: they have no site page and no .txt companion, so their links.page and links.text are null.

  • A motherboard names its socket (a socket summary; its summary carries socketSlug) and lists its pcieSlots by position from the top, each with its physicalLanes, its electricalLanes and its pcieGen. It has a status and offers, but no lowestPriceUsd or inStock.
  • A riser (a riser cable or vertical-mount kit) has its cable lengthMm, its pcieGen and its lanes, a status and offers, and no derived price or stock.
  • A socket is a reference value, not a product: its vendor, its segment and the motherboards built for it. It has no status, offers or provenance.

Three chassis fields point at them: expansionSlots (the case's expansion-slot positions), verticalMountSlots (the width in slots of its vertical GPU mount, or null when it has none) and, on the detail document only, bundledRiser (the riser that ships with the case, as a riser summary, or null). Details are on Collections and entities.

Fit and can-run in brief#

A fit verdict is one of three values: fits, check or does-not-fit. A card longer than the case's clearance, or wider in slots than the case allows, is does-not-fit. A card whose length leaves between 0 and 10 mm of clearance is check, as is a pair where a length or slot count isn't recorded. Each verdict carries its reasons (one for length, one for slots), and a maintainer override replaces the dimensional answer. A PSU advisory compares the case's included power supply with the card's recommended wattage; it never changes the verdict. Can-run compares the card's VRAM with the model's minimum VRAM and lists only models that fit, with the spare VRAM as headroomGb; when either value isn't recorded, the answer is determinable: false. The full rules are on Compatibility.

Changes#

/api/v1/changes lists every change record merged in the last 30 days, newest first, with each change's operation, entity, field and before and after values; an added or removed row also carries its whole contents as values. The window is chosen by merge time, so a record that merged late still appears; use the mergedAt timestamps as a polling cursor. A client that has been offline for longer than the 30-day window can't catch up from the feed: it must re-fetch every entity it watches. Older history is in the monthly archives at /api/v1/changes/{month}, which archive lists. See Changes feed.

Plain-text companions#

The API is JSON only. Plain text lives beside the web pages instead: every chip, board, chassis and model page, every catalog page and the home page has a .txt companion at the same path plus .txt, such as /boards/asus-rog-strix-4090.txt, /boards.txt or /boards/page/2.txt; the home page's is /index.txt. The blog, docs, legal and digest pages have them too. They are served as text/plain; charset=utf-8. Platforms, benchmarks, motherboards, risers and sockets have no web page, so they have no .txt either.

Two files help you find everything else:

  • /llms.txt indexes the .txt companions for AI agents.
  • /sitemap.xml lists every web page for crawlers, and /robots.txt points to it.

The .txt files are built from the same data and wording as the HTML, with these conventions:

  • Absolute UTC times. Each file has a Data as of: line with an ISO timestamp, such as 2026-10-04T06:12:00Z. Times in the body are written as 4 Oct 2026, 06:12 UTC, and dates as 12 Oct 2022. Relative times never appear, because the files are written at build time and must not age.
  • "Not recorded". A value that is null in the API appears as Not recorded. It is never left out, shown as 0, or written as N/A. A file whose data has no timestamps says Data as of: unknown.
  • Units are always written out: 24 GB, 450 W, 357.6 mm, 2,640 MHz, 384-bit, 3.5-slot, 128K tokens, 8B parameters. Numbers use comma thousands separators. Prices carry their currency symbol, such as $1,999.99 or €1,799.

Caching#

Every API response and every .txt file is sent with Cache-Control: public, max-age=300, so data can be up to five minutes older than the latest deploy. Each response has an ETag; send it back in If-None-Match and an unchanged file returns 304 Not Modified. An unchanged file keeps its ETag across deploys. Details, including the headers on redirects and errors, are on Caching, redirects and errors.

CORS#

Every response carries Access-Control-Allow-Origin: *, so browser code on any origin can read the API. Only simple requests work: a plain GET with no custom headers. Anything that triggers a CORS preflight fails, because the OPTIONS request returns 405. That includes setting If-None-Match yourself from browser code; let the browser's own cache revalidate instead. No credentials or cookies are involved.

Stability and versioning#

The /api/v1/ contract is frozen against breaking changes.

  • Additive changes can happen at any time: a new property on a response object, a new endpoint, or a new value in an open vocabulary such as verificationStatus. Ignore properties you don't recognise, and accept verificationStatus values you don't know.
  • Closed vocabularies won't grow in v1. Every enumerated value is fixed, including kind, status, op, rowOp, aspect, verdict, basis and the fit reason code. So are pageSize, the 10 mm check margin, the ordering rules and the fit and can-run rules.
  • Nothing changes shape in v1. No property is removed, renamed or retyped, and no field that is never null becomes nullable.
  • Breaking changes ship as /api/v2/, beside v1, with their own search index and OpenAPI document. v1 keeps being served until its traffic, measured at the edge, stops. A new version will be announced on this page.

The contract has a semantic version, 1.0.0 at launch: the minor number rises with each additive change and the patch number with editorial ones. It is the info.version of the OpenAPI document. The search index carries its own formatVersion, described under Search.

The compact search index at /api/v1/index holds one flat row per chip, board, chassis, model, motherboard and riser, with the fields most searches need. Download it once, cache it, revalidate it with its ETag, and filter it on your side. Platforms and benchmarks aren't in it. Sockets aren't either: there are few of them, and /api/v1/sockets lists them all.

Every entry has the same properties; a property that doesn't apply to its kind is always null (or [] for pciDeviceIds):

Fieldchipboardchassismodelmotherboardriser
slug, kind, name, statusYesYesYesYes (status is always active)YesYes
vendorDesignerBoard vendorVendorPublisherVendorVendor
chipSlugnullThe board's chipnullnullnullnull
vramGbMaximum VRAMVRAM fittednullMinimum VRAMnullnull
tdpWReference TDPBoard TDPnullnullnullnull
lengthMmnullThe card's lengthnullnullnullCable length
slotWidthnullThe card's slot widthnullnullnullnull
lowestPriceUsd, inStocknullFrom offersFrom offersnullnullnull
pciVendorId, pciDeviceIdsPCI IDsnull, []null, []null, []null, []null, []
socketnullnullnullnullIts socket's slugnull
pcieGennullnullnullnullnullPCIe generation
speedTokPerSecnullTokens per second on the speed referencenullnullnullnull
vramType, recommendedPsuW, widthMmnullMemory type, recommended PSU, height from the slotnullnullnullnull
powerConnectorsnull[{ "type", "count" }], [] when slot-powerednullnullnullnull

A case's clearances are deliberately left out of the index, so a "length at most" filter can never match a case; ask the compatibility endpoints instead. A board entry doesn't repeat its chip's vendor and name: read them from the chip entry whose slug is the board's chipSlug. Entries are ordered by kind (chip, board, chassis, model, motherboard, riser), then slug.

The envelope adds four fields to apiVersion, kind and asOf:

  • formatVersion is the index format as MAJOR.MINOR, 1.0 at launch. The major number equals the API version; the minor number rises when the index gains a property. Accept any 1.x.
  • datasetHash is sha256: plus 64 hex digits. It changes exactly when some published value changes, so you can compare it with a copy you hold.
  • speedReference names what speedTokPerSec measures: the benchmark llama-cpp-tg128 on the model llama-3-1-8b-instruct-q4-k-m.
  • counts gives the number of entries of each kind.

The same search library is used by the website, the MCP servers and the desktop app, and it is open source in the gpuninja-mcp repository. Its limits and defaults are:

Search factValueMeaning
LIMITS.defaultLimit20Results per page when you don't set limit
LIMITS.maxLimit1000The largest limit
LIMITS.maxTextLength200Characters of search text
LIMITS.maxTextTokens16Words of search text
LIMITS.maxSortKeys3Sort keys per query
LIMITS.maxFilterDepth8Nesting depth of a filter
LIMITS.maxFilterNodes64Nodes in a filter
LIMITS.maxInValues10000Values in one in filter
LIMITS.maxIssues20Validation issues reported for one query
DEFAULT_SORT.withText[{"by":"relevance","dir":"desc"},{"by":"name","dir":"asc"}]Default order when there is search text
DEFAULT_SORT.withoutText[{"by":"name","dir":"asc"}]Default order without search text
SORT_FIELDS.chip["relevance","name","vramGb","tdpW"]Sortable fields for chips
SORT_FIELDS.board["relevance","name","lowestPriceUsd","vramGb","tdpW","lengthMm","slotWidth","speedTokPerSec","pricePerVramGb","recommendedPsuW","widthMm","inStock"]Sortable fields for boards
SORT_FIELDS.chassis["relevance","name","lowestPriceUsd","inStock"]Sortable fields for cases
SORT_FIELDS.model["relevance","name","vramGb"]Sortable fields for models
SORT_FIELDS.motherboard["relevance","name"]Sortable fields for motherboards
SORT_FIELDS.riser["relevance","name","lengthMm","pcieGen"]Sortable fields for risers
includeDiscontinuedfalseDiscontinued entries are left out unless you ask for them

pricePerVramGb is worked out by the library from each board's lowestPriceUsd and vramGb; it is not in the index. Every order ends with slug ascending as a final tie-break. See Search index for text matching, filters and worked examples.

OpenAPI and generated clients#

https://gpuninja.com/api/v1/openapi.json is an OpenAPI 3.1.1 document with every path above and every response schema, as JSON Schema 2020-12. It is licensed Apache 2.0, so you may generate, publish and sell clients made from it; the data they fetch stays CC BY 4.0.

What the document tells a generator:

  • One GET per path, each with a stable operationId such as listBoards, listBoardsPage, getBoard, getBoardFits, getModelRunsOn, getSearchIndex, getChanges and getChangesArchive.
  • One tag per collection, plus search and changes, so generators that group operations by tag produce a BoardsApi, a ChipsApi and so on.
  • No authentication. security is an empty list, so a generated client sends no credentials.
  • One server, https://gpuninja.com. Point a client at another origin only for testing.
  • 200 has a JSON schema; 404 has none, because the body is the site's HTML error page. Treat a 404 as "no such resource" and don't parse it.
  • page is never a slug. /api/v1/boards/page/{page} and /api/v1/boards/{slug}/fits have the same shape, and some linters flag them as ambiguous. They aren't: page, index and 404 are reserved and never used as slugs.

For example, TypeScript types with openapi-typescript:

npx openapi-typescript https://gpuninja.com/api/v1/openapi.json -o gpuninja.d.ts

Or a client in another language with OpenAPI Generator:

npx @openapitools/openapi-generator-cli generate -g python \
  -i https://gpuninja.com/api/v1/openapi.json -o gpuninja-client

Remember the open-schema rule under Stability and versioning: new properties can appear at any time, and no schema forbids unknown ones. Generate in a mode that keeps or ignores unknown properties rather than rejecting the response, and regenerate when info.version rises to pick up new fields.

A generated API reference at /docs/api/reference, listing each endpoint and schema in a readable form, is planned. Until it ships, Collections and entities and the other pages of this section are the field reference.

In this section

  • Collections and entities

    The nine collections, how paging works, and the fields of every entity summary and detail document, with provenance and frozen records.

  • Compatibility

    Which cases a card fits, which cards fit a case, and which AI models a card can run, with the exact rules behind each answer.

  • Search index

    The compact search index of chips, boards, cases, models, motherboards and risers, its fields per kind, and how to search it with the open-source library.

  • Changes feed

    The machine-readable record of every data change in the last 30 days, the monthly archives, and how to poll them to keep a local copy current.

  • Caching, redirects and errors

    Response headers, ETag revalidation, CORS limits, redirects for renames and trailing slashes, and what a 404 or 405 looks like.