API reference
Overview
Query the composite, individual GPU prices, their counted votes, and the publication state behind each value.
Start
All endpoints are public GET requests under https://api.flopmark.com/v1. No API key is required.
Requests are limited to 60 per minute for each IP address.
curl https://api.flopmark.com/v1/index/currentEndpoints
/index/currentCurrent composite and the exact product rounds used to produce it.
/index/round/:roundIdOne composite round, resolved through its product round IDs.
/index/historyComposite round history, ordered by effective time.
/index/at?time=Composite round in effect at an ISO 8601 time, with its age and stale flag at that time.
/products/currentAll seven target products with current round and publication status.
/products/historyA bounded one-to-24-hour product history bundle for chart clients.
/products/:productId/currentCurrent valid round and status for one GPU product.
/products/:productId/round/:roundIdOne product round with every counted supply vote.
/products/:productId/historyValid round history for one GPU product.
/products/:productId/at?time=Product round in effect at an ISO 8601 time, with its age and stale flag at that time.
/rounds/:roundIdThe stored product or composite round exactly as published, for hash verification.
/receipts/:receiptIdThe immutable evaluation receipt a product round cites. It can exceed 1 MB, so it is streamed and gzip-compressed.
/receipts/:receiptId/observations?product_id=&offset=&limit=The observations a receipt cites, 200 per page at most, with nextOffset. product_id keeps only those routed to one product.
/evaluation/currentComposite status and all seven product publication states.
/methodologiesMethodology versions found in composite history.
/methodology/currentCurrent identity, composition rule, target products, and missing products.
/methodology/current/parametersEvery parameter behind the current methodology version, with a content hash.
/methodology/:version/parametersThe same parameter document for a released methodology version. 404 when its parameters are not published.
/rounds/:roundId/coverageFor a composite or product round: every expected provider and whether it voted, was suppressed, excluded, stale, or missing.
/evaluations/:receiptId/coverageThe same coverage report for one evaluation, including attempts that published no round.
/providers/currentLatest collection run and freshness for every provider.
/healthOperational calculation, product quorum, and provider freshness signals. Returns 503 when the monitor is silent or the composite is stale.
Methodology versions
/v1 identifies the response format. identity.methodologyVersion identifies the calculation used for a round.
GET /v1/methodologies
GET /v1/index/history?methodology=dense-bf16-v1.1.3
GET /v1/index/current?methodology=dense-bf16-v1.1.3
GET /v1/methodology/currentRead the current publication identity from /v1/methodology/current. Each composite also carries methodologyChange. For v1.1.0: the panel grows from seven to nine, quorum moves from six to seven, and verified eight-GPU H200 SXM5 offers may vote. Values remain unadjusted USD per dense BF16 PFLOP-hour.
Composite and products
Since v1.1.0, the composite may publish with seven of its nine target products. The response names every included and missing product and marks each input as fresh or retained. The historical v1.0.1 response below illustrates the older six-of-seven rule.
{
"data": {
"id": "sha256:…",
"effectiveAt": "2026-09-21T15:13:36.251Z",
"identity": {
"methodologyVersion": "dense-bf16-v1.0.1",
"policyVersion": "flopmark-policy-v1.0.1-tier-a"
},
"includedProductIds": ["…six products…"],
"missingProductIds": ["nvidia-geforce-rtx-4080-16gb"],
"result": {
"value": "3.402827633704",
"unit": "USD_PER_DENSE_BF16_PFLOP_HOUR",
"productCount": 6
},
"products": ["…six resolved product rounds…"]
}
}A product round reports its USD per GPU hour price, normalized price, counted supply votes, and distinct collecting providers separately.
curl https://api.flopmark.com/v1/products/nvidia-geforce-rtx-4090-24gb/currentProduct current, round, and history responses carry a dispersion object that shows how thin a price is. It is derived from the counted votes stored in the round, so it adds no new data and changes no price.
"dispersion": {
"voteCount": 3,
"collectorCount": 3,
"unit": "USD_PER_DENSE_BF16_PFLOP_HOUR",
"min": "…", "max": "…", "median": "…", "mad": "…",
"usdPerGpuHour": { "unit": "USD_PER_GPU_HOUR", "min": "…", "max": "…", "median": "…", "mad": "…" }
}voteCount is the number of counted votes. collectorCount is the number of distinct providers behind them, which can be lower because one provider may cast several upstream votes. min, max, median, and mad are decimal strings in the same unit as the published normalized price. median equals the published price. mad is the median absolute deviation from the median, with the same rounding as the price median. The nested usdPerGpuHour object gives the same statistics in USD per GPU hour. dispersion is null when a round stores no usable votes.
Freshness and settlement
/v1/index/current and /v1/products/:productId/current report the age of the value they return. All five fields are computed when the request is served.
{
"data": { "…": "…" },
"serverTime": "2026-10-21T12:00:00.000Z",
"asOf": "2026-10-21T11:58:30.000Z",
"ageSeconds": 90,
"servableUntil": "2026-10-21T12:28:30.000Z",
"stale": false
}asOf is the round's effective time. A round is servable for 1800 seconds after it, the staleAfterSeconds limit published in the status endpoints. At or past servableUntil the endpoint returns HTTP 503 instead of the price, with the last value in lastValue. Treat any non-200 response as no price.
HTTP/1.1 503 Service Unavailable
Cache-Control: private, no-store
{
"error": "The latest published value is past its allowed age and must not be used for settlement",
"stale": true,
"serverTime": "2026-10-21T12:40:00.000Z",
"asOf": "2026-10-21T11:58:30.000Z",
"ageSeconds": 2490,
"servableUntil": "2026-10-21T12:28:30.000Z",
"lastValue": { "…": "…" }
}A product with no published round also returns 503 with stale: true and a null lastValue. Fresh responses are cached for at most 15 seconds and never past servableUntil. /v1/index/current?methodology= naming a retired version is a history lookup: it returns HTTP 200 with stale: true and the same freshness fields. History, round, and listing endpoints are unchanged and are not freshness checked.
Every current response carries a state: /v1/index/current, /v1/products/:productId/current, and each entry of /v1/products/current. fresh: the latest attempt produced this round. carried: the latest attempt failed (a product needs 3 counted votes from 3 distinct collecting providers; the composite needs 7 of 9 products) and this retained round is served, still HTTP 200, for at most 1800 seconds from its effective time. A carried response adds lastFreshAt (the round's effective time, the last moment it was provably fresh; the 1800 second limit counts from it), carryLimitSeconds (1800), carryRemainingSeconds, and failure with attemptStatus, reason, and reasonCodes. A consumer that needs only fresh data should require state: "fresh".
suspended: nothing is servable. The single-product and index current endpoints return HTTP 503 whenever the state is suspended, with state: "suspended", suspendedReason, failure, lastRoundId, and lastRoundAt (both null when no round was ever published). Reasons: the round reached the 1800 second limit; identity-changed (a product round inside the limit that the publisher refused to retain because its publication identity no longer matches); status-unavailable (the status row or its latest attempt is missing, so the state cannot be established). A missing publication status is treated as suspended (HTTP 503, reason status-unavailable); this can briefly happen before the first publication cycle. Composite status stores no retention verdict, so the composite is gated by age and by status availability only. /v1/products/current is a status listing: it always returns 200, keeps currentRound, and marks a suspended entry with the same suspendedReason, failure, lastRoundId, and lastRoundAt. Never use the list to settle.
The /at endpoints add state, carryLimitSeconds, servableRemainingSecondsAtTime, and attemptHistoryAvailable. Attempt history is not stored, only rounds, so fresh versus carried at a past time cannot be told apart. state there is suspended when the round was 1800 seconds old or older at that time and servable otherwise. servable is an /at-only value outside the three-state ladder; ageSecondsAtTime bounds how long the value could have been carried.
/v1/health returns HTTP 503 with status unknown when no health signals exist, and down when the monitor has not written for 10 minutes or the latest composite is stale or missing. Open signals alone report degraded with HTTP 200.
History and precision
Prices are decimal strings. History contains stored rounds only; timestamps and prices are never interpolated.
History returns 100 rounds by default and accepts at most 250 per request. Pass nextCursor unchanged to fetch the next older page.
GET /v1/index/history?limit=250
GET /v1/index/history?limit=250&cursor=eyJlZmZlY3RpdmVBdCI6Ii4uLiJ9
GET /v1/products/nvidia-h100-pcie-80gb/history?limit=250For charts, range=24h or range=7d on /v1/index/history and /v1/products/history returns one stored round per bucket (10 minutes for 24h, 1 hour for 7d): the last round in the bucket, plus bucketStart and bucketCount (rounds in the bucket). Buckets with no rounds are omitted. On the index endpoint it cannot be combined with limit, before, or cursor; on the products endpoint it cannot be combined with hours.
GET /v1/index/history?range=7d
GET /v1/products/history?range=24hA range response also carries range (the requested window), resolutionSeconds (bucket width: 600 for 24h, 3600 for 7d), and count (points returned; summed across all products on the products endpoint). /v1/products/history keys data by product id, each value an array of points ordered by bucketStart.
To read the price in effect at a past time, use /at?time= with an ISO 8601 timestamp that has a timezone. It returns the latest round at or before that time, plus requestedTime, asOf, ageSecondsAtTime, and staleAtTime (true when the round was 1800 seconds old or older at that time). A round can be published after its effective time, so a time within a few minutes of now may later resolve to a newer round. It returns 404 before the first round and 400 for an invalid or future time.
GET /v1/index/at?time=2026-01-01T12:00:00Z
GET /v1/products/nvidia-h100-pcie-80gb/at?time=2026-01-01T12:00:00ZParameters
Every rule that decides which quotes can vote is published as one document, generated from the code that calculates the rounds. It covers quorum, the composite rule, freshness and carry limits, excluded quality flags, source policies and their exceptions, GPU-count rules, the dense BF16 TFLOPS behind each product, and the version identities. The connector mapping version and expected collection interval of each provider, and the sensitivity-analysis shock factors and capped-mean band (diagnostics only, used by no published price), are published too, in a separate operational section.
GET /v1/methodology/current/parameters
GET /v1/methodology/dense-bf16-v1.1.3/parameters{
"data": {
"methodologyVersion": "dense-bf16-v1.1.3",
"contentHash": "sha256:…",
"pinnedAt": "2026-10-29",
"hashInput": "sha256 of parameters.voting as JSON with keys sorted by UTF-16 code unit and no whitespace; parameters.operational is published but not hashed",
"parameters": {
"voting": {
"identity": { "methodologyVersion": "…", "policyVersion": "…", "hardwareRegistryVersion": "…" },
"quorum": { "minimumVotesPerProduct": 3, "minimumCollectingProvidersPerProduct": 3, "minimumInStockVotesPerProduct": 1 },
"composite": { "minimumProducts": 7, "weighting": "equal", "panelProductIds": ["…"] },
"staleness": { "productStaleAfterSeconds": 1800, "maximumSnapshotAgeSeconds": 86400 },
"eligibility": {
"rentalType": "on-demand",
"includedAvailability": ["available", "limited"],
"soldOutVotes": { "sourcePolicy": "direct", "windowSeconds": 21600, "windowEndsAt": "latest-provider-snapshot-finish-in-the-receipt" },
"excludedQualityFlagPolicy": ["…"],
"products": [{ "productId": "…", "allowedGpuCounts": [1], "excludedQualityFlags": ["…"] }]
},
"normalization": { "panel": [{ "productId": "…", "denseBf16TflopsFp32Accumulate": "…" }] },
"providers": [{ "providerId": "…", "staleAfterSeconds": 900, "sourcePolicy": "…" }]
},
"operational": {
"providers": { "…": { "mappingVersion": "…", "expectedCollectionIntervalSeconds": 300 } },
"diagnostics": { "providerShockFactorsBps": ["…"], "cappedMeanBandBps": { "floor": 7500, "ceiling": 12500 } }
}
}
}
}parameters.voting holds everything that decides what can vote and how a price is computed. contentHash covers only that section, and every change to it is a new methodology version. parameters.operational holds each provider's connector mapping version and expected collection interval, and the sensitivity diagnostics, which no published price uses. A connector fix that restores intended output changes the mapping version without a release, so operational can change while contentHash stays the same; treat only voting as immutable.
Quality flags are published as the calculation applies them. eligibility.excludedQualityFlagPolicy is the list of flags the calculation is given, and eligibility.products[].excludedQualityFlags is the list that actually excludes a quote for each product. They differ for hardware-variant-unknown, which excludes a quote only for products whose hardware registry entry names a variant: the RTX 3090, 4080, 4090 and 5090 have none, so the flag does not exclude their quotes.
current follows the methodology version of the composite the API is serving. A version is only served when its parameters are pinned, and other versions return 404. pinnedAt is the date the voting parameters of that version were first pinned. A version is pinned in its release commit; dense-bf16-v1.1.2 was already live when this endpoint was added, so it was pinned on 2026-10-29 from the code deployed that day, not at its release. Older versions are not published. The voting parameters of a pinned version never change. Parameters are published beside the rounds and are not copied into them, so no round, round ID or receipt changes.
Round coverage
/v1/rounds/:roundId/coverage accepts a composite or product round ID, with or without the sha256: prefix. It reports, for each product in the round, the providers the evaluation expected and what happened to each. /v1/index/current and /v1/products/:productId/current link to it in links.coverage.
An evaluation that fails publishes no round, but it still stores a receipt. /v1/evaluations/:receiptId/coverage reports on every product in that receipt, with each product's assessment (valid or insufficient-data) and a composition block with the fresh valid product count against the minimum. The receipt ID of the latest attempt is latestAttempt.receiptId in /v1/evaluation/current and in each product's status, so a failing round has the same report as a passing one.
GET /v1/rounds/sha256:…/coverage{
"data": {
"roundId": "sha256:…",
"kind": "composite",
"parameters": { "methodologyVersion": "dense-bf16-v1.1.3", "contentHash": "sha256:…" },
"products": [{
"productId": "nvidia-h100-pcie-80gb",
"roundId": "sha256:…",
"inputKind": "fresh-attempt",
"receipt": { "id": "sha256:…", "evaluatedAt": "2026-10-28T12:00:00.000Z" },
"quorum": { "minimumVotes": 3, "minimumCollectingProviders": 3, "countedVotes": 4, "collectingProviders": 4, "inStockVotes": 3, "soldOutVotes": 1, "minimumInStockVotes": 1, "met": true },
"counts": { "expectedProviders": 20, "voted": 4, "suppressed": 1, "excluded": 3, "noQuote": 9, "stale": 1, "missingSnapshot": 2 },
"providers": [{
"providerId": "vast",
"status": "excluded",
"quotes": { "matched": 4, "eligible": 0, "excluded": 4, "exclusionReasons": { "marketplace-machine-count-below-5": 4 } },
"votes": [],
"snapshot": { "runId": "…", "startedAt": "…", "finishedAt": "…", "quoteCount": 120 }
}]
}]
}
}Each provider has one status. voted: at least one of its votes is counted in the round. suppressed: it had an eligible vote, but an overlapping provider with higher priority was counted instead (votes[].suppressionReason names it). excluded: it quoted the product and every quote failed a rule; quotes.exclusionReasons counts the reasons. no-quote: its snapshot had no quote for the product. stale: no snapshot was inside its freshness limit, and lastSuccessAt is older than cutoff. missing-snapshot: no complete snapshot, with no good run to explain it; lastFailedRun shows a failed or partial run when there was one.
A composite lists all target products. A product with a published round reads the evaluation that round cites, so a retained input shows the older evaluation it came from. A product with no round reads the newest evaluation the composite cites and has roundId: null. The report is derived from stored receipts, round votes and collection runs; it changes no price. A round whose evaluation receipt is not stored in the API database, for example one written before the storage migration and never copied over, returns 404 with Evaluation receipt … is not available.
quorum.inStockVotes and quorum.soldOutVotes split the counted votes. A sold-out vote is a direct provider's current posted price for a product where it had a counted in-stock vote in a published valid round within the previous 21,600 seconds, and votes[].soldOut marks it. A round needs quorum.minimumInStockVotes in-stock votes, so a round of only sold-out votes has met: false. Evaluations before dense-bf16-v1.1.3 report soldOutVotes: 0 and minimumInStockVotes: 0.
stale has no age horizon: a provider whose last good run is older than its limit is stale with its lastSuccessAt, however long ago that was, and missing-snapshot means it has no good run before the evaluation at all or one inside its limit that was not selected. Receipts are large, so coverage is cached by receipt (receipts never change) and each API instance reads at most a few at once. When an instance is saturated it answers 503 with Retry-After: 5, and a round that cites more than 16 receipts returns 422. Query strings are ignored by the endpoint but are part of the CDN cache key, so add none.