Quote a media model
This page shows how to quote an image, speech, transcription, embedding or video model. It tells you which features to declare, how to declare sizes, and how to write the price table. Read Quote over the session first for the frames.
A buyer's control works only when an offer declares it. If you do not declare a control, no job that sets it reaches you. This is on purpose: an offer that ignores a control and still charges for the job is not a fair sale.
1. Declare the controls
Each control has a feature. The feature has the name of the request field of the buyer. Put the features that you support in supported_features of the quote.
| Kind | Field of the buyer | Feature |
|---|---|---|
| Image | seed |
image.seed |
| Image | background (transparent or opaque) |
image.background |
| Image | output_compression |
image.output_compression |
| Image | stream: true |
image.stream |
| Video | seed |
video.seed |
| Video | creativity |
video.creativity |
| Video | upscale_factor |
video.upscale_factor |
| Video | previous_job_id |
video.continue |
| Speech | instructions |
speech.instructions |
| Speech | input as more than one turn |
speech.turns |
| Transcription | diarize |
transcription.diarize |
| Transcription | keyterms |
transcription.keyterms |
| Embedding | input_type |
embedding.input_type |
- Declare a control only if your agent sends it to the upstream and the upstream honors it. If you declare a control that you ignore, you are paid for a job that the buyer did not get.
- A reference input has its own feature:
input.image,input.audio,input.videoorinput.file. Declare the kinds that your upstream reads. - The exchange drops a name that it does not know. A typo in a name means that you win no job with that control.
resolution,aspect_ratio,size,voice,qualityandgenerate_audioare not features. The exchange matches them with the sizes, voices and options that you registered. See Sizes and resolutions.
In an agent that you write
Send the features in the quote frame:
{ "type": "quote", "supported_features": ["text", "image.seed", "image.background"] }
With the Rust agent library, an Upstream lists its controls in Upstream::controls(kind, route, handle). It returns the features for that kind of job. The agent keeps only the controls that fit the kind. A rate card can narrow the list with ExecutionRoute::features. It cannot add to it. Upstream::reference_inputs(kind) lists the input.* features.
The Python reference provider declares only text. It cannot win a job that sets a control. Add the names that your upstream honors.
What the OpenAI adapter declares
The first-party OpenAI adapter declares these controls:
| Kind | Declared |
|---|---|
| Image | image.background and image.output_compression for a model whose name starts with gpt-image. image.seed when the route has a seed rule. |
| Video | video.seed when the route has a seed rule. |
| Speech | speech.instructions for a model whose name contains gpt-4o. |
It declares nothing else. In particular, it declares no image.stream, speech.turns, transcription control, embedding control, video.creativity, video.upscale_factor or video.continue. To sell those, write your own agent.
2. Declare sizes and resolutions
The registration of the deployment sets what the model makes. See Register deployments. The buyer's resolution and aspect_ratio resolve against it.
Images
List the pixel sizes in sizes of the image output, as <width>x<height>. There is no separate list of tiers or shapes. The exchange works them out from the sizes.
- The tier of a size is the tier that is nearest to its area. The tiers are
512,768,1K,1.5K,2Kand4K. For example,1024x1024,1536x1024and1024x1536are1K,1920x1080is1.5K, and2048x2048is2K. - A size has a shape when the two ratios differ by 1% or less.
- A buyer who sends
resolutionoraspect_ratiogets the declared size that has the tier and the shape. If more than one fits, the exchange picks the size that is nearest to the tier, then the one that is nearest to a square. - That size is the size of the dispatch, and it is the size that sets the price.
- An offer with no
sizescannot win a job that setssize,resolutionoraspect_ratio. - If no size fits, the buyer gets
400 unsupported_parameter, and the error lists the sizes that offers declare.
Video
List the pixel sizes in sizes and the labels in resolutions, for example ["1280x720"] and ["720p"].
- A
resolutionis a label. The exchange compares it with yourresolutionsas a string. An offer that lists none cannot win a job that setsresolution. - With
aspect_ratioand nosize, the exchange picks the smallest declared size that has the shape. If the buyer also sent a label such as720p, the short side of the size must be 720. - With
resolutionand noaspect_ratioand nosize, the exchange picks no size. The label only selects offers. - So to serve both fields, declare the sizes and the labels. An offer with
default_size_onlyset declares no size for this.
3. Write the price table
The quote states prices in pricing. A price is USD for one unit. Each line has billable, unit, usd and, if the price depends on an option, a variant.
billable |
unit |
Counted |
|---|---|---|
input_text |
mtok |
Input tokens that no cache served |
cache_read |
mtok |
Input tokens that a cache served |
cache_write_short |
mtok |
Tokens written to a cache for five minutes |
cache_write_long |
mtok |
Tokens written to a cache for one hour |
output_text |
mtok |
Output tokens, not the audio tokens |
output_audio |
mtok |
Audio output tokens of a chat answer |
output_image |
image |
Images |
output_speech |
character |
Characters of the text |
output_video |
second |
Seconds of video |
input_audio |
second |
Seconds of audio to transcribe |
input_reference_image |
image |
Reference images |
input_reference_video |
second |
Seconds of reference video |
input_reference_audio |
second |
Seconds of reference audio |
unitmust be the unit of the billable. A speech price of $15.00 per million characters is0.000015for the unitcharacter.- An image, speech, video or transcription offer must have one line with no variant for its own billable:
output_image,output_speech,output_videoorinput_audio. It cannot price the unit of another kind. - A reference price is optional. A reference image that you do not price is free. A job with reference video or audio goes only to an offer that prices it.
- A
variantis allowed onoutput_video, the threeinput_reference_*billables,output_imageandoutput_speech. It has the optionsresolution,audio,qualityandsize. A video takesresolutionandaudio. An image takesqualityandsize. Speech takesquality. - A variant line holds for a job that states every option the variant names. If more than one line holds, the line that names more options wins. If none holds, the line with no variant sets the price. A video job that does not state
generate_audiocounts asaudio: true. - A variant that names
audioneedsaudioasoptionalin thevideooutput. A variant that names aresolutionneeds that label inresolutions. A variant that names an imagesizeneeds that size insizes. Otherwise the quote is refused, because no job can reach the price.
The cap and the charge of a counted job are known before dispatch. The exchange multiplies the count by the line that holds for the job. So the cap is exactly the charge.
A video model
Registration:
{
"kind": "video_generation",
"output_modalities": [
{ "type": "text" },
{
"type": "video",
"sizes": ["1280x720", "1920x1080"],
"resolutions": ["720p", "1080p"],
"seconds": [4, 8],
"audio": "optional",
"formats": ["video/mp4"]
}
]
}
Quote:
"pricing": [
{ "billable": "output_video", "unit": "second", "usd": 0.10 },
{ "billable": "output_video", "unit": "second", "variant": { "audio": false }, "usd": 0.07 },
{ "billable": "output_video", "unit": "second", "variant": { "resolution": "720p" }, "usd": 0.06 },
{ "billable": "output_video", "unit": "second", "variant": { "resolution": "720p", "audio": false }, "usd": 0.04 },
{ "billable": "input_reference_video", "unit": "second", "usd": 0.02 }
]
A job for 8 seconds at 720p with sound pays the third line: 8 × $0.06. A job for 1080p with no other option pays the first line. A job for 720p without sound pays the fourth line, because it names more options than the second and the third.
An image model
Registration:
{
"kind": "image_generation",
"output_modalities": [
{ "type": "text" },
{
"type": "image",
"sizes": ["1024x1024", "1536x1024", "2048x2048"],
"formats": ["png", "webp"],
"max_count": 4
}
]
}
Quote:
"pricing": [
{ "billable": "output_image", "unit": "image", "usd": 0.04 },
{ "billable": "output_image", "unit": "image", "variant": { "size": "2048x2048" }, "usd": 0.08 }
]
A buyer who sends resolution: "2K" gets the size 2048x2048 and pays $0.08 for each image. A buyer who sends 1K gets 1024x1024 and pays $0.04. A buyer who sends quality or size pays the line whose variant matches, and the line with no variant otherwise.
If your upstream bills tokens, set pricing_unit to tokens and image_output_tokens_max in the registration. Quote input_text and output_text lines, and no output_image line. See Sell images.
A speech model
Registration lists the voices and formats. Quote one line, and a variant for a quality that costs more:
"pricing": [
{ "billable": "output_speech", "unit": "character", "usd": 0.000015 },
{ "billable": "output_speech", "unit": "character", "variant": { "quality": "hd" }, "usd": 0.00003 }
]
The charge is the characters of the text of all turns, times the price. A job for 500 characters pays $0.0075. With quality: "hd" it pays $0.015. A request with no quality, or a name that you do not price, pays the line with no variant.
A transcription model
Quote one line: { "billable": "input_audio", "unit": "second", "usd": 0.0001 }. The charge is the seconds of audio. See Sell transcription.
Old rate fields
An older quote states prices in fields such as rate_per_image, rate_per_video_second and pricing_skus. The exchange still reads them, but they are deprecated. No rate_* field is required.
- Send
pricingonly. It is the form that the ranking, the hold and the charge read. - If a quote has both forms, they must state the same prices. If they differ, the exchange refuses the quote with
invalid_termson the fieldpricing. For example:pricing states output_text at $2.000000000 and the old rate fields state output_text at $1.000000000. - Send both forms only to an exchange of an older release. It does not know
pricing.
4. What your agent receives
A media dispatch has one object for its kind: image, audio (speech), transcription, embedding or video. Control fields are present only if the buyer set them and you declared the feature.
- Image:
count,size,output_format,quality,response_format,seed,background,output_compressionandstream. Ifstreamis true, you can sendimage_partialevents. See Images. - Speech:
characters,voice,instructions,turns,response_format,speedandquality. See Speech. - Transcription:
seconds,format,language,timestamps,prompt,temperature,diarizeandkeyterms. Return onetranscriptevent. Withdiarize, give each segment aspeaker, such asspeaker_0. - Embeddings:
inputs,dimensionsandinput_type. Return one vector for each input. - Video:
seconds,size,audio,resolution,seed,creativity,upscale_factorandprevious_job_id. See Video.
For a video job, the exchange sends no cancel message. When the buyer cancels, or the job runs past the video job limit, it closes the dispatch connection. Watch for the close and stop. Each case costs the buyer nothing. See Cancel and expiry.
For previous_job_id, the exchange sends only the job id of the earlier video. It does not check that the earlier job ran on your deployment. Map the id to your own handle, and fail the job if you do not know it. See Continue a video.
5. What buyers see
- The model card lists a control in
parameterswhen at least one live offer declared the feature.seed,background,output_compression,stream,creativity,upscale_factor,previous_job_id,instructions,turns,diarize,keytermsandinput_typeare the keys. - It also lists
size,output_format,quality,resolution,secondsandgenerate_audiofrom your registration. Imageresolutionandaspect_ratioare not keys. Buyers read your sizes inimage_sizes. - It lists the cheapest line of each billable in
pricing. If a price depends on an option, the line shows thevariant. - The receipt of a job has
usage.pricing. Each line has the billable, the unit, the quantity and the provider charge. See Receipts. - A buyer who sets a control that no offer declared gets
400 unsupported_parameter. The error names the feature.
6. Checklist for a new model
- Register the deployment with the right
kindand theoutput_modalitiesof the model: sizes, voices, formats, lengths,resolutionsandaudio. - List only the sizes, voices and lengths that your upstream makes. A buyer can ask for any value you list.
- Choose the controls that your agent sends and your upstream honors. Declare their features in
supported_features. - Declare the
input.*features for the reference inputs that you read. - Write
pricing: one line with no variant for the billable of the kind, then the variant lines, then any reference prices. Use the unit of each billable. - Make sure that each variant names an option that the registration states.
- Send the quote. Read the
quote_ackor thequote_reject. Forinvalid_terms, readfieldandreason. - Quote as an unverified offer first. Buy one job from your own workspace for each control, and compare the result with the request. See Unverified offers.
- Open the model card. Check
parameters,image_sizesandpricing. - For video, test a cancel: the agent must stop when the dispatch closes.