Features answer the third of the three pricing questions — after what kind of files and how much content comes what you want to search by. Instead of choosing models or pipelines, you declare the capabilities you want — visual similarity (image_search), faces (faces), on-screen text (onscreen_text), document layout (document_layout) — and the platform resolves the implementation internally. Swapping or upgrading the models behind a feature never changes your config, your API calls, or your pricing.
Every feature applies to a modality (the what kind of files question) and is billed in that modality’s natural unit (the how much question):
| Modality | Unit | Base feature (included in base rate) | Add-on features |
|---|
image | per image | image_search | faces, multimodal_understanding |
video | per minute | video_search | faces, onscreen_text, audio_fingerprint, multimodal_understanding |
audio | per minute | audio_search | multimodal_understanding |
document | per page | document_search | faces, document_layout, multimodal_understanding |
text | per token | text_search | multimodal_understanding |
web | per crawled page | web_crawl | — |
Clustering and taxonomy enrichment are included on every modality at no additional charge. For rates, tiers, and how usage is billed, see Billing & Pricing.
Discover features
GET /v1/collections/features returns the live feature menu — modalities, units, feature keys, display names, and current rates. No authentication required. This endpoint is the source of truth: studio, the homepage pricing page, and the billing engine all read the same catalog.
curl "https://api.mixpeek.com/v1/collections/features"
{
"modalities": [
{
"modality": "video",
"unit": "minute",
"unit_display": "per minute",
"base": {
"key": "video_search",
"name": "Video search (scene embeddings)",
"kind": "base",
"included_in_base_rate": true,
"rate_usd": 0.05,
"per": 1
},
"addons": [
{ "key": "faces", "name": "Face detection + identity", "kind": "addon", "rate_usd": 0.10, "per": 1, "companion": true },
{ "key": "onscreen_text", "name": "On-screen text (video OCR)", "kind": "addon", "rate_usd": 0.10, "per": 1, "companion": true },
{ "key": "clustering", "name": "Clustering (included)", "kind": "included", "included": true, "pricing_note": "included — no additional charge" }
]
}
],
"custom_features": [],
"full_res_multiplier": 2.0
}
Response abbreviated — the live payload covers all six modalities. Feature kind tells you how it’s priced:
| Kind | Meaning |
|---|
base | Covered by the modality’s per-unit base rate — you get it by creating any collection for that modality |
addon | Priced per the same unit, added on top of the base rate |
external | Backed by external LLM inference — usage-based passthrough pricing, never flat-rated |
included | Bundled enrichment (clustering, taxonomy) — no additional charge |
Create a collection with features
Pass features: [...] to Create Collection. The platform resolves the pipeline, pins its version, and defaults the input wiring for you:
curl -X POST "https://api.mixpeek.com/v1/collections" \
-H "Authorization: Bearer $MIXPEEK_API_KEY" \
-H "X-Namespace: $NAMESPACE_ID" \
-H "Content-Type: application/json" \
-d '{
"collection_name": "press-photos",
"source": { "type": "bucket", "bucket_ids": ["'$BUCKET_ID'"] },
"features": ["image_search"]
}'
Both SDKs accept features on collection create — Python mixpeek>=1.3.29 (client.collections.create(...)) and JavaScript mixpeek>=0.81.28 (client.collections.createCollection({ createCollectionRequest: { ... } })). Set the namespace when you construct the client (Python namespace=, JS namespace:, or the MIXPEEK_NAMESPACE env var) — collection create is namespace-scoped and returns 403 without it. The feature_extractor field is a deprecated alias.
Every organization’s scaffolded uploads bucket exposes one standard source property per modality — image, video, audio, pdf, content (text), and url (web). A features-based create maps the pipeline’s inputs to those properties automatically, so the collection is runnable with zero wiring.
If your bucket uses different property names, the create returns a teaching 422 naming the fields that exist so you can rename the property — or fall back to explicit input_mappings via the advanced pipeline config.
Errors teach
Validation on the features path is designed for humans and agents guessing their way in — every rejection tells you what to do instead:
- Unknown key —
features: ["face"] responds with the full list of valid feature keys (and reminds you about custom:<plugin>).
- One pipeline per collection — some feature combinations need separate collections (e.g. a base feature plus a
companion: true add-on). The error lists exactly which features group into which collection: create one collection per group over the same source bucket. Add-ons flagged companion: true in discovery always create a companion collection alongside the base.
features XOR feature_extractor — provide one or the other, never both.
- Not yet available — feature keys that appear on the roadmap but haven’t shipped are rejected explicitly rather than silently ignored.
Custom features (bring your own)
Custom extractors you publish are your vocabulary — they stay explicitly named and are selected as custom:<plugin_name>:
{
"collection_name": "product-defects",
"source": { "type": "bucket", "bucket_ids": ["bkt_123"] },
"features": ["custom:defect_detector"]
}
Custom features are priced per unit from the compute profile the plugin declares — the same machinery that prices native features. See Custom Extractors.
Full resolution opt-out
By default, Mixpeek normalizes content once at ingest (video to a 720p mezzanine, images capped at ~1568px max edge, audio to 16kHz mono) — originals are always kept untouched in your bucket. If a workload needs extraction on the original resolution (fine print OCR, tiny logos), opt out per collection:
{
"collection_name": "fine-print",
"source": { "type": "bucket", "bucket_ids": ["bkt_123"] },
"features": ["document_layout"],
"full_res": true
}
Full-res processing bills at a 2x multiplier on the modality’s base and add-on rates (full_res_multiplier in the pricing payload).
Chain collections (advanced)
Searching by one thing often produces content worth searching by another — e.g. search video by scenes, then feed those documents into a second collection to search the results by something else. Collection-to-collection pipelines do exactly this: a collection can read another collection’s documents as its source, forming a processing DAG. See Multi-Tier Feature Extraction.
Find your feature_uri (to search)
A feature_search stage needs a feature_uri — the exact vector index to search. You picked features by name; each one produces a vector index whose feature_uri looks like mixpeek://<extractor>@<version>/<model>.
The last segment is the embedding model name, not the literal word embedding. A wrong feature_uri (e.g. .../embedding) does not error — the stage matches nothing and silently returns 0 results (with a self-correcting warning in the response). Always use the exact string.
The reliable, version-proof way — read it from the collection. After processing, GET the collection and copy the URI; this always matches what you actually built, even if model versions change:
curl "https://api.mixpeek.com/v1/collections/$COLLECTION_ID" \
-H "Authorization: Bearer $MIXPEEK_API_KEY" \
-H "X-Namespace: $NAMESPACE_ID"
# → vector_indexes[].feature_uri e.g. "mixpeek://universal_extractor@v1/gemini-embedding-2"
Common features → feature_uri (the exact string a fresh @v1 collection produces):
Feature (features: [...]) | Extractor | feature_uri |
|---|
video_search | universal_extractor | mixpeek://universal_extractor@v1/gemini-embedding-2 |
text_search | text_extractor | mixpeek://text_extractor@v1/multilingual_e5_large_instruct_v1 |
multimodal_understanding | multimodal_extractor | mixpeek://multimodal_extractor@v1/vertex_multimodal_embedding |
Video transcript search. video_search produces one blended scene embedding (gemini-embedding-2); the transcript rides along as a text payload field, not a separate searchable vector. To search a transcript as its own vector, add the multimodal_understanding feature with transcription enabled — it creates a second index mixpeek://multimodal_extractor@v1/multilingual_e5_large_instruct_v1. When in doubt, GET the collection and read the feature_uris it actually has.
Estimate before you run
POST /v1/organizations/billing/estimate quotes planned ingestion using the same rating engine that bills you — the quote and the charge can’t disagree:
curl -X POST "https://api.mixpeek.com/v1/organizations/billing/estimate" \
-H "Authorization: Bearer $MIXPEEK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "mime_type": "video/mp4", "minutes": 42 },
{ "mime_type": "application/pdf", "pages": 300 }
],
"features": ["faces", "onscreen_text"]
}'
{
"pricing_model": "v2",
"placeholder_rates": false,
"line_items": [
{ "modality": "video", "key": "video_search", "units": 42.0, "rate_usd": 0.05, "per": 1, "amount_usd": 2.10 },
{ "modality": "video", "key": "faces", "units": 42.0, "rate_usd": 0.10, "per": 1, "amount_usd": 4.20 },
{ "modality": "video", "key": "onscreen_text", "units": 42.0, "rate_usd": 0.10, "per": 1, "amount_usd": 4.20 },
{ "modality": "document", "key": "document_search", "units": 300.0, "rate_usd": 1.50, "per": 1000, "amount_usd": 0.45 },
{ "modality": "document", "key": "faces", "units": 300.0, "rate_usd": 1.00, "per": 1000, "amount_usd": 0.30 }
],
"total_usd": 11.25,
"batch_minimum_applied": false,
"allowance_pool_usd": 10.0,
"allowance_covered_usd": 10.0,
"estimated_overage_usd": 1.25
}
Base features are quoted automatically per the modality of each item — you only list add-ons. Unknown MIME types quote at the cheapest band rather than failing (be forgiving is a design rule here).
Under the hood every feature resolves to an extraction pipeline, and custom extractors let you register your own. But built-in extractor names are no longer part of the public API surface — they remain accepted in configs only as deprecated aliases. If you have existing configs using feature_extractor, see the migration guide; for advanced pipeline knobs (input mappings, field passthrough, parameters), see Pipeline Configuration.