Skip to navigation

Price Comparison API

Price Comparison API

POST /v1/price-comparisons compares the BeatAPI price of one published pricing spec with the public prices other platforms list for the same spec. It needs no API key and is not billed.

The comparison is neutral. BeatAPI is ranked by the same rules as every other platform, and when another platform is cheaper the answer says so: beatapi_position is not_lowest.

Quick start

curl https://api.beatapi.io/v1/price-comparisons \
-H "Content-Type: application/json" \
-d '{"model":"Nano Banana Pro"}'

With only model, the model’s common configuration is compared with every platform that has a stored quote for it. Add spec to choose another configuration, and providers to choose the platforms.

Request

FieldTypeRequiredDescription
modelstringYes, unless spec is sentThe model’s display name (Nano Banana Pro), its page slug, the shared prefix of its spec IDs, or one spec ID. Matching ignores case, and dots and dashes are interchangeable.
specstringNoOne spec ID of that model from GET /v1/pricing, such as nano-banana-pro-4k. Without it the common configuration is compared.
providersstring[]NoPlatform IDs to compare, at most 20. Without it every platform with a stored quote for the spec takes part, BeatAPI included.

model and spec are each at most 200 characters. When model is empty, spec is used to find the model.

The common configuration

Without spec, one configuration is chosen by its shape, never by which platform wins:

  • Text models: input tokens.
  • Video models: the first 720p spec without reference video or video-to-video input.
  • Image models: the first plain image spec, for example nano-banana-pro-1k.

When model is itself a spec ID, that spec is compared.

Spec IDs

Spec IDs come from the anonymous price list: each entry of data.groups[].specs[] in GET https://api.beatapi.io/v1/pricing has an id. Every comparison also returns the model’s available_specs.

Platform IDs

Platform IDs are lowercase, as returned in quotes[].provider; beatapi is BeatAPI. When you send providers, only the listed platforms are compared, so include beatapi to rank BeatAPI. A listed platform that has no quote for this spec is returned as pending_verification. An ID the comparison table does not know is rejected with 400.

Response

A successful call returns 200 with the result in data, the same envelope as GET /v1/pricing. The response is not cached (Cache-Control: no-store).

The example below uses prices checked on 2026-11-11. Prices, dates and the data_version value change over time; call the endpoint for current figures.

curl https://api.beatapi.io/v1/price-comparisons \
-H "Content-Type: application/json" \
-d '{"model":"Nano Banana Pro","spec":"nano-banana-pro-1k","providers":["beatapi","atlascloud","fal","google","higgsfield","wavespeed"]}'
{
"data": {
"status": "ready",
"model": "Nano Banana Pro",
"spec": {
"id": "nano-banana-pro-1k",
"label": "1 image · 1K",
"label_zh": "1 张图片 · 1K",
"category": "image",
"unit": "image"
},
"available_specs": ["nano-banana-pro-1k", "nano-banana-pro-2k", "nano-banana-pro-4k"],
"questions": [],
"quotes": [
{
"provider": "beatapi",
"status": "quoted",
"provider_model_id": "Nano Banana Pro",
"amount": "0.05",
"currency": "USD",
"quote_kind": "fixed",
"matched_configuration": "1 image · 1K",
"source_urls": ["https://api.beatapi.io/v1/pricing", "https://beatapi.io/nano-banana-pro-api"],
"checked_at": "2026-11-11T10:00:00Z",
"expires_at": null,
"freshness_status": "fresh",
"rank_eligible": true,
"exclusion_reason": null
},
{
"provider": "atlascloud",
"status": "quoted",
"provider_model_id": "google/nano-banana-pro/text-to-image",
"amount": "0.14",
"currency": "USD",
"quote_kind": "estimate",
"matched_configuration": "1k · 1:1 · 1 张 · 文生图 · 不开 web/image search · thinking 默认",
"source_urls": ["https://www.atlascloud.ai/models/google/nano-banana-pro/text-to-image"],
"checked_at": "2026-11-11T08:59:27Z",
"expires_at": "2026-12-11T08:59:27Z",
"freshness_status": "fresh",
"rank_eligible": false,
"exclusion_reason": "Estimated by the provider's calculator, not a fixed price."
},
{
"provider": "fal",
"status": "quoted",
"provider_model_id": "fal-ai/nano-banana-pro",
"amount": "0.15",
"currency": "USD",
"quote_kind": "fixed",
"matched_configuration": "Nano Banana Pro text-to-image resolution=1K, no web search",
"source_urls": ["https://fal.ai/models/fal-ai/nano-banana-pro"],
"checked_at": "2026-11-11T08:50:26Z",
"expires_at": "2026-12-11T08:50:26Z",
"freshness_status": "fresh",
"rank_eligible": true,
"exclusion_reason": null
},
{
"provider": "google",
"status": "quoted",
"provider_model_id": "gemini-3-pro-image",
"amount": "0.1344",
"currency": "USD",
"quote_kind": "formula",
"matched_configuration": "gemini-3-pro-image text-to-image 1K 1:1, Standard paid tier, 1 output image",
"source_urls": ["https://ai.google.dev/gemini-api/docs/pricing#gemini-3-pro-image"],
"checked_at": "2026-11-11T08:50:17Z",
"expires_at": "2026-12-11T08:50:17Z",
"freshness_status": "fresh",
"rank_eligible": true,
"exclusion_reason": null
},
{
"provider": "higgsfield",
"status": "unsupported_configuration",
"amount": null,
"currency": "USD",
"source_urls": ["https://open.higgsfield.ai/pricing"],
"checked_at": "2026-11-11T08:48:09Z",
"expires_at": "2026-12-11T08:48:09Z",
"freshness_status": "fresh",
"rank_eligible": false,
"exclusion_reason": "The provider does not offer this model or configuration."
},
{
"provider": "wavespeed",
"status": "quoted",
"provider_model_id": "google/nano-banana-pro/text-to-image",
"amount": "0.126",
"currency": "USD",
"quote_kind": "fixed",
"matched_configuration": "1k · 1:1 · 1 张 · 文生图 · 不开 web/image search",
"source_urls": ["https://wavespeed.ai/models/google/nano-banana-pro/text-to-image"],
"checked_at": "2026-11-11T08:52:36Z",
"expires_at": "2026-12-11T08:52:36Z",
"freshness_status": "fresh",
"rank_eligible": true,
"exclusion_reason": null
}
],
"summary": {
"winner_providers": ["beatapi"],
"lowest_amount": "0.05",
"ranking_scope": ["beatapi", "wavespeed", "google", "fal"],
"excluded_providers": ["atlascloud", "higgsfield"],
"coverage_complete": true,
"beatapi_position": "lowest",
"conclusion": "beatapi has the lowest price of 4 verified providers at $0.05.",
"benchmarks": {
"provider_count": 4,
"mean_scope": ["beatapi", "wavespeed", "google", "fal"],
"mean_amount": "0.1151",
"second_provider_amount": "0.126",
"second_provider_providers": ["wavespeed"],
"lowest_below_second_percentage": "60.32",
"lowest_below_mean_percentage": "56.56"
}
},
"data_version": "quotes-684-1794391200",
"generated_at": "2026-11-11T10:00:00Z"
}
}

How the figures in this example follow from the quotes:

  • Four quotes are ranked: 0.05, 0.126, 0.1344 and 0.15. The estimate from atlascloud and the unsupported configuration at higgsfield are listed but not ranked.
  • Lowest: 0.05 (beatapi). Second position: 0.126 (wavespeed). (0.126 − 0.05) ÷ 0.126 = 60.32%.
  • Mean: (0.05 + 0.126 + 0.1344 + 0.15) ÷ 4 = 0.1151. (0.1151 − 0.05) ÷ 0.1151 = 56.56%.

Top-level fields

FieldDescription
statusneeds_clarification, ready, partial or unavailable (see below).
modelThe model that was compared, as named in the price list.
model_candidatesOnly with needs_clarification: models to send back as model, at most 10.
specWhat was compared: id, label, label_zh, category and unit. null with needs_clarification.
available_specsEvery spec ID of the model, for a follow-up call with spec.
questionsWhat to send next when the request is ambiguous; otherwise empty.
quotesOne entry per platform, beatapi first and the rest by ID.
summaryThe ranking and the four metrics. null with needs_clarification.
data_versionChanges whenever the stored quotes change. Compare it between calls; do not parse it.
generated_atWhen the answer was computed, in UTC.
statusMeaning
needs_clarificationThe model was not recognised, or more than one model matches. quotes is empty and summary is null; send one of model_candidates.
readyEvery listed platform has a final answer for the spec.
partialAt least one quote is pending_verification, source_conflict, stale or incomplete_price. The ranking covers only the quotes that can be ranked.
unavailableNothing to compare. Handle it like an answer without a ranking.

Units

spec.unitEvery amount is the price of
imageOne image of the spec.
videoOne complete video of the length the spec label states, not one second.
1M_tokensOne million tokens of the spec, for example input tokens.

All amounts are in USD and are decimal strings, so a small price is never rounded to zero. null means there is no trustworthy price; "0" would mean free.

Quotes

FieldDescription
providerPlatform ID.
statusquoted, stale, incomplete_price, unsupported_configuration, no_public_price, source_conflict or pending_verification.
provider_model_idThe platform’s own model or endpoint name, when known.
amountThe full price for one unit, or null.
quote_kindfixed (a listed price), formula (computed from listed rates) or estimate (a calculator estimate).
matched_configurationThe configuration the price was read for, as written when it was checked.
noteAn optional remark, such as a time-of-day multiplier on the BeatAPI price.
source_urlsPublic pages the price was read from.
checked_at / expires_atWhen the price was checked, and when it stops being ranked (30 days later). The BeatAPI quote is read live, so its checked_at is the time of the call and expires_at is null.
freshness_statusfresh, stale or unknown.
rank_eligibleWhether the quote takes part in the ranking.
exclusion_reasonWhy the quote is listed but not ranked; null when it is ranked.

A quote is ranked only when it has an amount, was checked within the last 30 days, matches the spec exactly and is not an estimate. Every other quote stays in quotes with its exclusion_reason, so you can see who was left out and why.

The BeatAPI quote is the live retail price from GET /v1/pricing, so a BeatAPI price change shows up in the next comparison. Other platforms’ prices are checked by hand against their public pages.

Summary and the four metrics

FieldDescription
winner_providers / lowest_amountThe lowest ranked price and every platform at that price. A tie lists all of them.
benchmarks.lowest_below_second_percentageHow far the lowest price is below the price in second position. Ties count, so a tie for lowest gives "0.00". second_provider_amount and second_provider_providers show that price and who has it.
benchmarks.mean_amountThe mean of every ranked price, BeatAPI included, one price per platform (mean_scope).
benchmarks.lowest_below_mean_percentageHow far the lowest price is below that mean.
benchmarks.provider_countHow many platforms are ranked.
ranking_scope / excluded_providersRanked platforms from lowest to highest, and the listed platforms that are not ranked.
coverage_completefalse when the answer is partial.
beatapi_positionlowest, tied_lowest, not_lowest, insufficient_comparison or not_compared.
conclusionOne English sentence that states the result.

Percentages are strings with two decimal places, computed from unrounded amounts and rounded half up at the end. A metric is null when fewer than two platforms are ranked or its denominator is zero; it is never NaN. mean_amount is rounded to 8 decimal places.

beatapi_positionMeaning
lowestBeatAPI alone has the lowest ranked price.
tied_lowestBeatAPI shares the lowest price with at least one other platform.
not_lowestAnother platform is cheaper.
insufficient_comparisonBeatAPI is ranked but no other platform is, so there is nothing to compare.
not_comparedBeatAPI is not ranked, for example because providers did not include beatapi.

When the model is ambiguous

A name that matches several models returns 200 with needs_clarification:

curl https://api.beatapi.io/v1/price-comparisons \
-H "Content-Type: application/json" \
-d '{"model":"banana pro"}'
{
"data": {
"status": "needs_clarification",
"model_candidates": ["Nano Banana Pro", "Nano Banana Pro Standard"],
"spec": null,
"questions": ["Which model do you want to compare? Send one of model_candidates as model."],
"quotes": [],
"summary": null,
"data_version": "quotes-684-1794391200",
"generated_at": "2026-11-11T10:00:00Z"
}
}

Send one candidate unchanged as model. An exact name, slug or spec ID always wins over a partial match, so Nano Banana Pro resolves to one model. A name that matches no model also returns needs_clarification, without model_candidates.

Errors

Errors use the standard error object. A request you must change returns 400 bad_request with a message that names the problem:

Causemessage
The body is not valid JSONInvalid price comparison request.
spec does not belong to the modelspec "nano-banana-2-1k" is not a spec of Nano Banana Pro; see GET /v1/pricing for its specs.
An unknown platform in providersunknown provider "example".
A platform listed twiceprovider "fal" is listed twice.
model or spec is too longmodel and spec must be at most 200 characters.
More than 20 platformsproviders may list at most 20 entries.

Do not resend a 400 unchanged. A 500 internal_error means the comparison could not be computed; retry once, then contact support with request_id.